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
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use std::error::Error;

pub fn generate_binary(dialect: &Ident, names: &[Ident]) -> Result<TokenStream, Box<dyn Error>> {
    let mut stream = TokenStream::new();

    for name in names {
        let document = create_document(dialect, name);
        let operation_name = create_operation_name(dialect, name);

        stream.extend(TokenStream::from(quote! {
            #[doc = #document]
            pub fn #name<'c>(
                lhs: crate::ir::Value<'c, '_>,
                rhs: crate::ir::Value<'c, '_>,
                location: crate::ir::Location<'c>,
            ) -> crate::ir::Operation<'c> {
                binary_operator(#operation_name, lhs, rhs, location)
            }
        }));
    }

    stream.extend(TokenStream::from(quote! {
        fn binary_operator<'c>(
            name: &str,
            lhs: crate::ir::Value<'c, '_>,
            rhs: crate::ir::Value<'c, '_>,
            location: crate::ir::Location<'c>,
        ) -> crate::ir::Operation<'c> {
            crate::ir::operation::OperationBuilder::new( name, location)
                .add_operands(&[lhs, rhs])
                .enable_result_type_inference()
                .build()
                .expect("valid operation")
        }
    }));

    Ok(stream)
}

pub fn generate_unary(dialect: &Ident, names: &[Ident]) -> Result<TokenStream, Box<dyn Error>> {
    let mut stream = TokenStream::new();

    for name in names {
        let document = create_document(dialect, name);
        let operation_name = create_operation_name(dialect, name);

        stream.extend(TokenStream::from(quote! {
            #[doc = #document]
            pub fn #name<'c>(
                value: crate::ir::Value<'c, '_>,
                location: crate::ir::Location<'c>,
            ) -> crate::ir::Operation<'c> {
                unary_operator(#operation_name, value, location)
            }
        }));
    }

    stream.extend(TokenStream::from(quote! {
        fn unary_operator<'c>(
            name: &str,
            value: crate::ir::Value<'c, '_>,
            location: crate::ir::Location<'c>,
        ) -> crate::ir::Operation<'c> {
            crate::ir::operation::OperationBuilder::new( name, location)
                .add_operands(&[value])
                .enable_result_type_inference()
                .build()
                .expect("valid operation")
        }
    }));

    Ok(stream)
}

pub fn generate_typed_unary(
    dialect: &Ident,
    names: &[Ident],
) -> Result<TokenStream, Box<dyn Error>> {
    let mut stream = TokenStream::new();

    for name in names {
        let document = create_document(dialect, name);
        let operation_name = create_operation_name(dialect, name);

        stream.extend(TokenStream::from(quote! {
            #[doc = #document]
            pub fn #name<'c>(
                value: crate::ir::Value<'c, '_>,
                r#type: crate::ir::Type<'c>,
                location: crate::ir::Location<'c>,
            ) -> crate::ir::Operation<'c> {
                typed_unary_operator(#operation_name, value, r#type, location)
            }
        }));
    }

    stream.extend(TokenStream::from(quote! {
        fn typed_unary_operator<'c>(
            name: &str,
            value: crate::ir::Value<'c, '_>,
            r#type: crate::ir::Type<'c>,
            location: crate::ir::Location<'c>,
        ) -> crate::ir::Operation<'c> {
            crate::ir::operation::OperationBuilder::new( name, location)
                .add_operands(&[value])
                .add_results(&[r#type])
                .build()
                .expect("valid operation")
        }
    }));

    Ok(stream)
}

fn create_document(dialect: &Ident, name: &Ident) -> String {
    format!(
        " Creates an `{}` operation.",
        create_operation_name(dialect, name)
    )
}

fn create_operation_name(dialect: &Ident, name: &Ident) -> String {
    format!("{}.{}", dialect, name)
}