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}