wasmer_inline_c_macro/
lib.rs

1//! Please see the `inline-c` crate to learn more.
2
3use proc_macro2::TokenStream;
4use quote::quote;
5
6/// Execute a C program and return a `Result` of
7/// `wasmer_inline_c::Assert`. See examples inside the `inline-c` crate.
8#[proc_macro]
9pub fn assert_c(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10    let input = TokenStream::from(input);
11    let input_as_string = reconstruct(input);
12
13    quote!(
14        wasmer_inline_c::run(wasmer_inline_c::Language::C, #input_as_string).map_err(|e| panic!("{}", e)).unwrap()
15    )
16    .into()
17}
18
19/// Execute a C++ program and return a `Result` of
20/// `wasmer_inline_c::Assert`. See examples inside the `inline-c` crate.
21#[proc_macro]
22pub fn assert_cxx(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
23    let input = TokenStream::from(input);
24    let input_as_string = reconstruct(input);
25
26    quote!(
27        wasmer_inline_c::run(wasmer_inline_c::Language::Cxx, #input_as_string).map_err(|e| panic!("{}", e)).unwrap()
28    )
29    .into()
30}
31
32fn reconstruct(input: TokenStream) -> String {
33    use proc_macro2::{Delimiter, Spacing, TokenTree::*};
34
35    let mut output = String::new();
36    let mut iterator = input.into_iter().peekable();
37
38    loop {
39        match iterator.next() {
40            Some(Punct(token)) => {
41                let token_value = token.as_char();
42
43                match token_value {
44                    '#' => {
45                        output.push('\n');
46                        output.push(token_value);
47
48                        match iterator.peek() {
49                            // #include …
50                            Some(Ident(include)) if *include == "include" => {
51                                iterator.next();
52
53                                match iterator.next() {
54                                    // #include <…>
55                                    Some(Punct(punct)) => {
56                                        if punct.as_char() != '<' {
57                                            panic!(
58                                                "Invalid opening token after `#include`, received `{:?}`.",
59                                                token
60                                            )
61                                        }
62
63                                        output.push_str("include <");
64
65                                        loop {
66                                            match iterator.next() {
67                                                Some(Punct(punct)) => {
68                                                    let punct = punct.as_char();
69
70                                                    if punct == '>' {
71                                                        break;
72                                                    }
73
74                                                    output.push(punct)
75                                                }
76
77                                                Some(Ident(ident)) => {
78                                                    output.push_str(&ident.to_string())
79                                                }
80
81                                                token => panic!(
82                                                    "Invalid token in `#include` value, with `{:?}`.",
83                                                    token
84                                                ),
85                                            }
86                                        }
87
88                                        output.push('>');
89                                        output.push('\n');
90                                    }
91
92                                    // #include "…"
93                                    Some(Literal(literal)) => {
94                                        output.push_str("include ");
95                                        output.push_str(&literal.to_string());
96                                        output.push('\n');
97                                    }
98
99                                    Some(token) => panic!(
100                                        "Invalid opening token after `#include`, received `{:?}`.",
101                                        token
102                                    ),
103
104                                    None => panic!("`#include` must be followed by `<` or `\"`."),
105                                }
106                            }
107
108                            // #define, only available on nightly.
109                            Some(Ident(define)) if *define == "define" => {
110                                #[cfg(not(nightly))]
111                                panic!(
112                                    "`#define` in C is only supported in `inline-c` with Rust nightly"
113                                );
114
115                                #[cfg(nightly)]
116                                {
117                                    let current_line = define.span().start().line;
118                                    iterator.next();
119                                    output.push_str("define ");
120
121                                    loop {
122                                        match iterator.peek() {
123                                            Some(item) => {
124                                                if item.span().start().line == current_line {
125                                                    output.push_str(&item.to_string());
126                                                    iterator.next();
127                                                } else {
128                                                    output.push('\n');
129                                                    break;
130                                                }
131                                            }
132
133                                            None => break,
134                                        }
135                                    }
136                                }
137                            }
138
139                            _ => (),
140                        }
141                    }
142
143                    ';' => {
144                        output.push(token_value);
145                        output.push('\n');
146                    }
147
148                    _ => {
149                        output.push(token_value);
150
151                        if token.spacing() == Spacing::Alone {
152                            output.push(' ');
153                        }
154                    }
155                }
156            }
157
158            Some(Ident(ident)) => {
159                output.push_str(&ident.to_string());
160                output.push(' ');
161            }
162
163            Some(Group(group)) => {
164                let group_output = reconstruct(group.stream());
165
166                match group.delimiter() {
167                    Delimiter::Parenthesis => {
168                        output.push('(');
169                        output.push_str(&group_output);
170                        output.push(')');
171                    }
172
173                    Delimiter::Brace => {
174                        output.push('{');
175                        output.push('\n');
176                        output.push_str(&group_output);
177                        output.push('\n');
178                        output.push('}');
179                    }
180
181                    Delimiter::Bracket => {
182                        output.push('[');
183                        output.push_str(&group_output);
184                        output.push(']');
185                    }
186
187                    Delimiter::None => {
188                        output.push_str(&group_output);
189                    }
190                }
191            }
192
193            Some(token) => {
194                output.push_str(&token.to_string());
195            }
196
197            None => break,
198        }
199    }
200
201    output
202}