1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
use crate::base::{
    ast::{
        self, Alternative, Argument, Expr, ExprField, Pattern, TypeBinding, TypedIdent,
        ValueBinding,
    },
    pos,
    symbol::{Symbol, Symbols},
    types::{ctor_args, remove_forall, row_iter, Type, TypeContext},
};

use crate::macros::Error;

use crate::derive::*;

pub fn generate<'ast>(
    mut arena: ast::ArenaRef<'_, 'ast, Symbol>,
    symbols: &mut Symbols,
    bind: &TypeBinding<'ast, Symbol>,
) -> Result<ValueBinding<'ast, Symbol>, Error> {
    let span = bind.name.span;

    let x = Symbol::from("x");
    let show_fn = TypedIdent::new(symbols.simple_symbol("show_"));

    let show_expr = match **remove_forall(bind.alias.value.unresolved_type()) {
        Type::Variant(ref variants) => {
            let alts: Vec<_> = row_iter(variants)
                .map(|variant| {
                    let pattern_args: Vec<_> = ctor_args(&variant.typ)
                        .enumerate()
                        .map(|(i, field)| {
                            (
                                is_self_type(&bind.alias.value.name, field),
                                TypedIdent::new(Symbol::from(format!("arg_{}", i))),
                            )
                        })
                        .collect();

                    let expr = {
                        let open_brace = literal(span, variant.name.declared_name());

                        pattern_args
                            .iter()
                            .fold(open_brace, |acc, &(self_type, ref x)| {
                                let show_function = if self_type {
                                    show_fn.name.clone()
                                } else {
                                    symbols.simple_symbol("show")
                                };
                                let show = arena.infix(
                                    span,
                                    literal(span, "("),
                                    symbols.simple_symbol("++"),
                                    arena.infix(
                                        span,
                                        arena.app(
                                            span,
                                            show_function,
                                            vec![ident(span, x.name.clone())],
                                        ),
                                        symbols.simple_symbol("++"),
                                        literal(span, ")"),
                                    ),
                                );
                                arena.infix(
                                    span,
                                    acc,
                                    symbols.simple_symbol("++"),
                                    arena.infix(
                                        span,
                                        literal(span, " "),
                                        symbols.simple_symbol("++"),
                                        show,
                                    ),
                                )
                            })
                    };

                    let ctor_pattern = |pattern_args: Vec<_>| {
                        pos::spanned(
                            span,
                            Pattern::Constructor(
                                TypedIdent::new(variant.name.value.clone()),
                                arena.alloc_extend(
                                    pattern_args
                                        .into_iter()
                                        .map(|arg| pos::spanned(span, Pattern::Ident(arg))),
                                ),
                            ),
                        )
                    };
                    Alternative {
                        pattern: ctor_pattern(pattern_args.into_iter().map(|t| t.1).collect()),
                        expr,
                    }
                })
                .collect();
            Expr::Match(
                arena.alloc(ident(span, x.clone())),
                arena.alloc_extend(alts),
            )
        }
        Type::Record(ref row) => {
            let field_symbols: Vec<_> = row_iter(row)
                .map(|field| {
                    TypedIdent::new(Symbol::from(format!("{}", field.name.declared_name())))
                })
                .collect();

            let expr = {
                let open_brace = literal(span, "{ ");

                let show_expr = field_symbols
                    .iter()
                    .enumerate()
                    .fold(open_brace, |acc, (i, x)| {
                        let show = arena.app(
                            span,
                            symbols.simple_symbol("show"),
                            vec![ident(span, x.name.clone())],
                        );

                        let show_field = arena.infix(
                            span,
                            acc,
                            symbols.simple_symbol("++"),
                            arena.infix(
                                span,
                                literal(span, &format!("{} = ", x.name.declared_name())),
                                symbols.simple_symbol("++"),
                                show,
                            ),
                        );

                        let last = i == field_symbols.len() - 1;
                        let suffix = if last { " " } else { ", " };
                        arena.infix(
                            span,
                            show_field,
                            symbols.simple_symbol("++"),
                            literal(span, suffix),
                        )
                    });

                arena.infix(
                    span,
                    show_expr,
                    symbols.simple_symbol("++"),
                    literal(span, "}"),
                )
            };
            Expr::Match(
                arena.alloc(ident(span, x.clone())),
                arena.alloc_extend(Some(Alternative {
                    pattern: arena.generate_record_pattern(span, row, field_symbols),
                    expr,
                })),
            )
        }
        _ => return Err(Error::message("Unable to derive Show for this type")),
    };

    let mut self_type = {
        let mut arena = arena;
        move || bind.alias.value.self_type(&mut arena)
    };

    let show_record_expr = Expr::rec_let_bindings(
        arena,
        Some(ValueBinding {
            name: pos::spanned(span, Pattern::Ident(show_fn.clone())),
            args: arena.alloc_extend(Some(Argument::explicit(pos::spanned(
                span,
                TypedIdent::new(x.clone()),
            )))),
            expr: pos::spanned(span, show_expr),
            metadata: Default::default(),
            typ: Some(arena.clone().function(vec![self_type()], arena.string())),
            resolved_type: Type::hole(),
        }),
        pos::spanned(
            span,
            Expr::Record {
                typ: Type::hole(),
                types: &mut [],
                exprs: arena.alloc_extend(Some(ExprField {
                    metadata: Default::default(),
                    name: pos::spanned(span, symbols.simple_symbol("show")),
                    value: Some(ident(span, show_fn.name.clone())),
                })),
                base: None,
            },
        ),
    );

    Ok(ValueBinding {
        name: pos::spanned(
            span,
            Pattern::Ident(TypedIdent::new(symbols.simple_symbol(format!(
                "show_{}",
                bind.alias.value.name.declared_name()
            )))),
        ),
        args: &mut [],
        expr: pos::spanned(span, show_record_expr),
        metadata: Default::default(),
        typ: Some(binding_type(arena, symbols, "Show", self_type(), bind)),
        resolved_type: Type::hole(),
    })
}