1use aleph_syntax_tree::syntax::AlephTree as at;
2use aleph_syntax_tree::types::{Type, Variant};
3use aleph_syntax_tree::effects::Effect;
4
5fn gen_type(ty: &Type) -> String {
8 match ty {
9 Type::Int => "Int".to_string(),
10 Type::Float => "Float".to_string(),
11 Type::Bool => "Bool".to_string(),
12 Type::String => "String".to_string(),
13 Type::Bytes => "Bytes".to_string(),
14 Type::Unit => "Unit".to_string(),
15 Type::Var{name} => name.clone(),
16 Type::Fun{params, ret} => format!(
17 "({}) -> {}",
18 params.iter().map(gen_type).collect::<Vec<String>>().join(", "),
19 gen_type(ret)
20 ),
21 Type::List{elem} => format!("List<{}>", gen_type(elem)),
22 Type::Tuple{elems} => format!(
23 "({})",
24 elems.iter().map(gen_type).collect::<Vec<String>>().join(", ")
25 ),
26 Type::Record{fields} => format!(
27 "{{{}}}",
28 fields.iter()
29 .map(|f| format!("{}: {}", f.name, gen_type(&f.ty)))
30 .collect::<Vec<String>>()
31 .join(", ")
32 ),
33 Type::Sum{variants} => variants.iter().map(gen_variant).collect::<Vec<String>>().join(" | "),
34 }
35}
36
37fn gen_variant(v: &Variant) -> String {
40 if v.fields.is_empty() {
41 v.name.clone()
42 } else {
43 format!(
44 "{}({})",
45 v.name,
46 v.fields.iter().map(gen_type).collect::<Vec<String>>().join(", ")
47 )
48 }
49}
50
51fn gen_params(args: Vec<Box<at>>, param_types: &[Type]) -> String {
57 args.into_iter().enumerate().map(|(i, a)| {
58 let name = match *a {
59 at::Typed{inner, ..} => gen(*inner, 0),
60 other => gen(other, 0),
61 };
62 match param_types.get(i) {
63 Some(ty) => format!("{}: {}", name, gen_type(ty)),
64 None => name,
65 }
66 }).collect::<Vec<String>>().join(", ")
67}
68
69fn gen_effect(e: &Effect) -> &'static str {
72 match e {
73 Effect::Pure => "pure",
74 Effect::Io => "io",
75 Effect::Net => "net",
76 Effect::Mut => "mut",
77 Effect::Act => "act",
78 }
79}
80
81fn gen(ast: at, indent: i64) -> String {
82 let c_indent=aleph_syntax_tree::comp_indent(indent);
83 match ast {
84 at::Unit => format!("{}", ""),
85 at::Ellipsis => format!("{}", ""),
86 at::Int{value} => format!("{}{}", c_indent, value),
87 at::Float{value} => format!("{}{}", c_indent, value),
88 at::Bool{value} => format!("{}{}", c_indent, value),
89 at::String{value} => format!("{}{}", c_indent, value),
90 at::Ident{value} => format!("{}{}", c_indent, value),
91 at::Complex{real, imag} => format!("{}{} + ({} *j)", c_indent, real, imag),
92 at::Bytes{elems} => format!("{}", String::from_utf8(elems).expect("Found invalid UTF-8")),
93 at::Tuple{elems} => format!("{}", aleph_syntax_tree::gen_list_expr_sep(elems, gen, ", ")),
94 at::Array{elems} => format!("[{}]", aleph_syntax_tree::gen_list_expr_sep(elems, gen, ", ")),
95 at::Neg{expr} => format!("{}-{}", c_indent, gen(*expr, 0)),
96 at::Not{bool_expr} => format!("{}!({})", c_indent, gen(*bool_expr, 0)),
97 at::And{bool_expr1, bool_expr2} => format!("{}{} & {}", c_indent, gen(*bool_expr1, 0), gen(*bool_expr2, 0)),
98 at::Or{bool_expr1, bool_expr2} => format!("{}{} | {}", c_indent, gen(*bool_expr1, 0), gen(*bool_expr2, 0)),
99 at::Add{number_expr1, number_expr2} => format!("{}{} + {}", c_indent, gen(*number_expr1, 0), gen(*number_expr2, 0)),
100 at::Sub{number_expr1, number_expr2} => format!("{}{} - {}", c_indent, gen(*number_expr1, 0), gen(*number_expr2, 0)),
101 at::Mul{number_expr1, number_expr2} => format!("{}{} * {}", c_indent, gen(*number_expr1, 0), gen(*number_expr2, 0)),
102 at::Div{number_expr1, number_expr2} => format!("{}{} / {}", c_indent, gen(*number_expr1, 0), gen(*number_expr2, 0)),
103 at::Eq{expr1, expr2} => format!("{}{} = {}", c_indent, gen(*expr1, 0), gen(*expr2, 0)),
104 at::LE{expr1, expr2} => format!("{}{} <= {}", c_indent, gen(*expr1, 0), gen(*expr2, 0)),
105 at::In{expr1, expr2} => format!("{}{} in {}", c_indent, gen(*expr1, 0), gen(*expr2, 0)),
106 at::If{condition, then,els} => match *els {
107 at::Unit => format!("{c_indent}({cond})?{{\n{then}\n{c_indent}}}", c_indent=c_indent, cond=gen(*condition, 0), then=gen(*then, indent+1)),
108 _ => format!("{c_indent}({cond})?{{\n{then}\n{c_indent}}}:{{\n{els}\n{c_indent}}}", c_indent=c_indent, cond=gen(*condition, 0), then=gen(*then, indent+1), els=gen(*els, indent+1)),
109 },
110 at::While{init_expr, condition, loop_expr, post_expr} => {
111 format!("{}\n{}({})?*{{\n{}\n{}\n{}}}", gen(*init_expr, indent), c_indent, gen(*condition, 0), gen(*loop_expr, indent+1), gen(*post_expr, indent+1), c_indent)
112 },
113 at::Let{var, is_pointer, value, expr} => match *expr {
114 at::Unit{} => format!("{}{}{} = {};", c_indent, var, (if is_pointer=="true" {":"} else {""}), gen(*value, 0)),
115 _ => format!("{}{}{} = {};\n{}", c_indent, var, (if is_pointer=="true" {":"} else {""}), gen(*value, 0), gen(*expr, indent)),
116 },
117 at::LetRec{name, args, body} => format!("{}fun {}({}) = {{\n{}\n{}}}", c_indent, name, aleph_syntax_tree::gen_list_expr_sep(args, gen, ", "), gen(*body, indent+1), c_indent),
118 at::Get{array_name, elem} => format!("{}{}[{}]", c_indent, array_name, gen(*elem, 0)),
119 at::Put{array_name, elem, value, insert} => format!("{}{}[{}{}] = {}", c_indent, array_name, (if insert=="true" {"+"} else {""}), gen(*elem, 0), gen(*value, 0)),
120 at::Remove{array_name, elem, is_value} => format!("{}{}[{}{}]", c_indent, array_name, (if is_value=="true" {"-"} else {"/"}), gen(*elem, 0)),
121 at::Length{var} => format!("{}|{}|", c_indent, var),
122 at::Match{expr, case_list} => format!("{}match {} with\n{}", c_indent, gen(*expr, 0), aleph_syntax_tree::gen_list_expr(case_list, gen)),
123 at::MatchLine{condition, case_expr} => format!("{}: {} -> {}\n", c_indent, gen(*condition, 0), gen(*case_expr, 0)),
124 at::Var{var, is_pointer} => format!("{}{}{}",c_indent, (if is_pointer=="true" {"!"} else {""}), var),
125 at::App{object_name, fun, param_list} => format!("{}{}{}({})",c_indent, (if object_name.ne("") {format!("{}.", object_name)} else {String::from("")}), gen(*fun, 0), aleph_syntax_tree::gen_list_expr_sep(param_list, gen, ", ")),
126 at::Stmts{expr1, expr2} => format!("{};\n{}", gen(*expr1, indent), gen(*expr2, indent)),
127 at::Iprt{name, ..} => format!("{}import {}", c_indent, name),
128 at::Clss{name, attribute_list, body, ..} => format!("{}class {} {{\n{}{};\n{}\n}}", c_indent, name, aleph_syntax_tree::comp_indent(indent+1), attribute_list.join(&format!(";\n{}", aleph_syntax_tree::comp_indent(indent+1))), gen(*body, indent+1)),
129 at::Return{value} => format!("return {}", gen(*value, 0)),
130 at::Comment{value} => format!("{}{}", c_indent, value),
131 at::CommentMulti{value} => format!("{}{}", c_indent, value),
132 at::Intend{name, params, body} => format!(
134 "{}intention {}({}) = {{\n{}\n{}}}",
135 c_indent, name,
136 aleph_syntax_tree::gen_list_expr_sep(params, gen, ", "),
137 gen(*body, indent+1),
138 c_indent
139 ),
140 at::Suggest{var, context, options, ..} => {
141 if options.is_empty() {
142 format!("{}suggest {} from {}", c_indent, var, gen(*context, 0))
143 } else {
144 format!("{}suggest {} from {} : [{}]", c_indent, var, gen(*context, 0),
145 aleph_syntax_tree::gen_list_expr_sep(options, gen, ", "))
146 }
147 },
148 at::Act{intention, effect} => match effect {
149 None => format!("{}act {}", c_indent, gen(*intention, 0)),
150 Some(eff) => format!("{}act {} as {}", c_indent, gen(*intention, 0), gen(*eff, 0)),
151 },
152 at::Remember{key, value, ttl} => match ttl {
153 None => format!("{}remember {} = {}", c_indent, gen(*key, 0), gen(*value, 0)),
154 Some(t) => format!("{}remember {} = {} for {}", c_indent, gen(*key, 0), gen(*value, 0), gen(*t, 0)),
155 },
156 at::Perceive{source, var, ..} => match var {
157 None => format!("{}perceive {}", c_indent, gen(*source, 0)),
158 Some(v) => format!("{}perceive {} as {}", c_indent, gen(*source, 0), v),
159 },
160 at::TypeDef{name, variants} => format!(
161 "{}type {} = {}",
162 c_indent, name, variants.iter().map(gen_variant).collect::<Vec<String>>().join(" | ")
163 ),
164 at::Typed{inner, ty} => match (*inner, ty) {
172 (at::LetRec{name, args, body}, Type::Fun{params, ret}) => format!(
173 "{}fun {}({}) -> {} = {{\n{}\n{}}}",
174 c_indent, name, gen_params(args, ¶ms), gen_type(&ret), gen(*body, indent+1), c_indent
175 ),
176 (fallback_inner, fallback_ty) => format!("{}({}: {})", c_indent, gen(fallback_inner, 0), gen_type(&fallback_ty)),
177 },
178 at::WithEffects{inner, effects} => {
186 let effs_str = effects.iter().map(gen_effect).collect::<Vec<&str>>().join(", ");
187 match *inner {
188 at::Typed{inner: fn_inner, ty: Type::Fun{params, ret}} => match *fn_inner {
189 at::LetRec{name, args, body} => format!(
190 "{}fun {}({}) -> {} | {} = {{\n{}\n{}}}",
191 c_indent, name, gen_params(args, ¶ms), gen_type(&ret), effs_str, gen(*body, indent+1), c_indent
192 ),
193 other => format!("{} | {}", gen(other, indent), effs_str),
194 },
195 other => format!("{} | {}", gen(other, indent), effs_str),
196 }
197 },
198 _ => todo!()
199 }
200}
201
202pub fn generate(ast: at) -> String {
203 gen(ast, 0)
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use aleph_syntax_tree::effects::EffectSet;
210
211 #[test]
212 fn prints_a_sum_type_declaration() {
213 let node = at::TypeDef {
214 name: "Shape".to_string(),
215 variants: vec![
216 Variant { name: "Circle".to_string(), fields: vec![Type::Float] },
217 Variant { name: "Rect".to_string(), fields: vec![Type::Float, Type::Float] },
218 ],
219 };
220 assert_eq!(generate(node), "type Shape = Circle(Float) | Rect(Float, Float)");
221 }
222
223 #[test]
224 fn prints_a_zero_field_variant_without_parens() {
225 let node = at::TypeDef {
226 name: "Bool2".to_string(),
227 variants: vec![
228 Variant { name: "T".to_string(), fields: vec![] },
229 Variant { name: "F".to_string(), fields: vec![] },
230 ],
231 };
232 assert_eq!(generate(node), "type Bool2 = T | F");
233 }
234
235 #[test]
236 fn gen_type_prints_primitives() {
237 assert_eq!(gen_type(&Type::Int), "Int");
238 assert_eq!(gen_type(&Type::Float), "Float");
239 assert_eq!(gen_type(&Type::Bool), "Bool");
240 assert_eq!(gen_type(&Type::String), "String");
241 assert_eq!(gen_type(&Type::Bytes), "Bytes");
242 assert_eq!(gen_type(&Type::Unit), "Unit");
243 }
244
245 #[test]
246 fn gen_type_prints_a_var_reference() {
247 assert_eq!(gen_type(&Type::Var { name: "Shape".to_string() }), "Shape");
248 }
249
250 #[test]
251 fn gen_type_prints_a_function_type() {
252 let ty = Type::Fun {
253 params: vec![Type::Int, Type::Bool],
254 ret: Box::new(Type::String),
255 };
256 assert_eq!(gen_type(&ty), "(Int, Bool) -> String");
257 }
258
259 #[test]
260 fn gen_type_prints_a_list_type() {
261 let ty = Type::List { elem: Box::new(Type::Int) };
262 assert_eq!(gen_type(&ty), "List<Int>");
263 }
264
265 #[test]
266 fn gen_type_prints_a_tuple_type() {
267 let ty = Type::Tuple { elems: vec![Type::Int, Type::String] };
268 assert_eq!(gen_type(&ty), "(Int, String)");
269 }
270
271 #[test]
272 fn gen_type_prints_a_record_type() {
273 use aleph_syntax_tree::types::RecordField;
274 let ty = Type::Record {
275 fields: vec![RecordField { name: "x".to_string(), ty: Type::Float }],
276 };
277 assert_eq!(gen_type(&ty), "{x: Float}");
278 }
279
280 #[test]
281 fn prints_a_typed_zero_arg_function() {
282 let node = at::Typed {
283 inner: Box::new(at::LetRec {
284 name: "hello".to_string(),
285 args: Vec::new(),
286 body: Box::new(at::String { value: "\"hi\"".to_string() }),
287 }),
288 ty: Type::Fun { params: Vec::new(), ret: Box::new(Type::String) },
289 };
290 assert_eq!(generate(node), "fun hello() -> String = {\n \"hi\"\n}");
291 }
292
293 #[test]
294 fn prints_a_typed_function_with_typed_params() {
295 let node = at::Typed {
296 inner: Box::new(at::LetRec {
297 name: "square".to_string(),
298 args: vec![Box::new(at::Typed {
299 inner: Box::new(at::Ident { value: "n".to_string() }),
300 ty: Type::Int,
301 })],
302 body: Box::new(at::Mul {
303 number_expr1: Box::new(at::Ident { value: "n".to_string() }),
304 number_expr2: Box::new(at::Ident { value: "n".to_string() }),
305 }),
306 }),
307 ty: Type::Fun { params: vec![Type::Int], ret: Box::new(Type::Int) },
308 };
309 assert_eq!(generate(node), "fun square(n: Int) -> Int = {\n n * n\n}");
310 }
311
312 #[test]
313 fn prints_a_bare_typed_value_generically() {
314 let node = at::Typed {
315 inner: Box::new(at::Ident { value: "n".to_string() }),
316 ty: Type::Int,
317 };
318 assert_eq!(generate(node), "(n: Int)");
319 }
320
321 #[test]
322 fn prints_a_typed_function_with_multiple_params() {
323 let node = at::Typed {
324 inner: Box::new(at::LetRec {
325 name: "add".to_string(),
326 args: vec![
327 Box::new(at::Typed { inner: Box::new(at::Ident { value: "a".to_string() }), ty: Type::Int }),
328 Box::new(at::Typed { inner: Box::new(at::Ident { value: "b".to_string() }), ty: Type::Int }),
329 ],
330 body: Box::new(at::Add {
331 number_expr1: Box::new(at::Ident { value: "a".to_string() }),
332 number_expr2: Box::new(at::Ident { value: "b".to_string() }),
333 }),
334 }),
335 ty: Type::Fun { params: vec![Type::Int, Type::Int], ret: Box::new(Type::Int) },
336 };
337 assert_eq!(generate(node), "fun add(a: Int, b: Int) -> Int = {\n a + b\n}");
338 }
339
340 #[test]
341 fn params_list_shorter_than_args_degrades_to_bare_names_past_the_end() {
342 let node = at::Typed {
348 inner: Box::new(at::LetRec {
349 name: "f".to_string(),
350 args: vec![
351 Box::new(at::Typed { inner: Box::new(at::Ident { value: "n".to_string() }), ty: Type::Int }),
352 Box::new(at::Ident { value: "m".to_string() }),
353 ],
354 body: Box::new(at::Ident { value: "n".to_string() }),
355 }),
356 ty: Type::Fun { params: vec![Type::Int], ret: Box::new(Type::Int) },
357 };
358 assert_eq!(generate(node), "fun f(n: Int, m) -> Int = {\n n\n}");
359 }
360
361 #[test]
362 fn generic_fallback_handles_a_multiline_inner_without_panicking() {
363 let node = at::Typed {
364 inner: Box::new(at::If {
365 condition: Box::new(at::Bool { value: "true".to_string() }),
366 then: Box::new(at::Int { value: "1".to_string() }),
367 els: Box::new(at::Int { value: "2".to_string() }),
368 }),
369 ty: Type::Int,
370 };
371 assert_eq!(generate(node), "((true)?{\n 1\n}:{\n 2\n}: Int)");
375 }
376
377 #[test]
378 fn out_of_range_arg_with_its_own_type_annotation_is_still_printed_bare() {
379 let node = at::Typed {
384 inner: Box::new(at::LetRec {
385 name: "f".to_string(),
386 args: vec![
387 Box::new(at::Typed { inner: Box::new(at::Ident { value: "n".to_string() }), ty: Type::Int }),
388 Box::new(at::Typed { inner: Box::new(at::Ident { value: "m".to_string() }), ty: Type::String }),
389 ],
390 body: Box::new(at::Ident { value: "n".to_string() }),
391 }),
392 ty: Type::Fun { params: vec![Type::Int], ret: Box::new(Type::Int) },
393 };
394 assert_eq!(generate(node), "fun f(n: Int, m) -> Int = {\n n\n}");
395 }
396
397 #[test]
398 fn prints_a_typed_function_with_one_effect() {
399 let node = at::WithEffects {
400 inner: Box::new(at::Typed {
401 inner: Box::new(at::LetRec {
402 name: "square".to_string(),
403 args: vec![Box::new(at::Typed {
404 inner: Box::new(at::Ident { value: "n".to_string() }),
405 ty: Type::Int,
406 })],
407 body: Box::new(at::Mul {
408 number_expr1: Box::new(at::Ident { value: "n".to_string() }),
409 number_expr2: Box::new(at::Ident { value: "n".to_string() }),
410 }),
411 }),
412 ty: Type::Fun { params: vec![Type::Int], ret: Box::new(Type::Int) },
413 }),
414 effects: EffectSet::from([Effect::Pure]),
415 };
416 assert_eq!(generate(node), "fun square(n: Int) -> Int | pure = {\n n * n\n}");
417 }
418
419 #[test]
420 fn prints_multiple_effects_comma_separated_in_declaration_order() {
421 let node = at::WithEffects {
422 inner: Box::new(at::Typed {
423 inner: Box::new(at::LetRec {
424 name: "main".to_string(),
425 args: Vec::new(),
426 body: Box::new(at::Unit),
427 }),
428 ty: Type::Fun { params: Vec::new(), ret: Box::new(Type::Unit) },
429 }),
430 effects: EffectSet::from([Effect::Net, Effect::Io]),
431 };
432 assert_eq!(generate(node), "fun main() -> Unit | io, net = {\n\n}");
440 }
441
442 #[test]
443 fn effects_on_a_non_function_typed_value_fall_back_generically() {
444 let node = at::WithEffects {
445 inner: Box::new(at::Typed {
446 inner: Box::new(at::Ident { value: "n".to_string() }),
447 ty: Type::Int,
448 }),
449 effects: EffectSet::from([Effect::Mut]),
450 };
451 assert_eq!(generate(node), "(n: Int) | mut");
452 }
453
454 #[test]
455 fn effects_on_a_non_typed_value_fall_back_generically() {
456 let node = at::WithEffects {
457 inner: Box::new(at::Ident { value: "n".to_string() }),
458 effects: EffectSet::from([Effect::Mut]),
459 };
460 assert_eq!(generate(node), "n | mut");
461 }
462}