i8051_proc_macro/
lib.rs

1#![doc=include_str!("../README.md")]
2
3use std::collections::BTreeSet;
4
5use proc_macro::TokenStream;
6use proc_macro2::Span;
7use quote::quote;
8use syn::{
9    Expr, ExprAssign, ExprBinary, ExprBlock, ExprCall, ExprField, ExprIf, ExprLet, ExprLit,
10    ExprMethodCall, ExprParen, ExprPath, ExprTuple, ExprUnary, Local, Path, PathArguments,
11    PathSegment, Token, parse_macro_input, punctuated::Punctuated,
12};
13
14/// Given `unique!(next token...)`, renders `next!(token...)`, keeping only the unique tokens.
15#[proc_macro]
16pub fn unique(input: TokenStream) -> TokenStream {
17    let mut unique = BTreeSet::new();
18    let mut input = input.into_iter();
19    let next = TokenStream::from(input.next().expect("Expected a next-macro token"));
20    let next = parse_macro_input!(next as syn::Ident);
21    for token in input {
22        let token = TokenStream::from(token);
23        let token = parse_macro_input!(token as syn::Ident);
24        unique.insert(token);
25    }
26    TokenStream::from(quote! { #next ! ( #(#unique)* ); })
27}
28
29/// Desugars and transforms a block of statements into a sequence of
30/// op_def_read/of_def_write calls.
31#[proc_macro]
32pub fn op_def(input: TokenStream) -> TokenStream {
33    let mut input = input.into_iter();
34    let context = input
35        .next()
36        .expect("First argument must be the CPU reference");
37    let input = TokenStream::from_iter(input);
38    let context = TokenStream::from(context);
39    let context = parse_macro_input!(context as syn::Ident);
40    let input = parse_macro_input!(input as syn::Block);
41
42    let transformed_stmts = input
43        .stmts
44        .into_iter()
45        .map(|stmt| transform_stmt(&context, &stmt))
46        .collect::<Vec<_>>();
47
48    let output = quote! {
49        #(#transformed_stmts)*
50    };
51
52    TokenStream::from(output)
53}
54
55fn transform_block(context: &syn::Ident, block: &syn::Block) -> proc_macro2::TokenStream {
56    let transformed_stmts = block
57        .stmts
58        .iter()
59        .map(|stmt| transform_stmt(context, stmt))
60        .collect::<Vec<_>>();
61    quote! {
62        {
63            #(#transformed_stmts)*
64        }
65    }
66}
67
68fn transform_stmt(context: &syn::Ident, stmt: &syn::Stmt) -> proc_macro2::TokenStream {
69    match stmt {
70        syn::Stmt::Expr(expr, semi) => {
71            let transformed = transform_expr(context, expr);
72            quote!(#transformed #semi)
73        }
74        syn::Stmt::Local(Local {
75            let_token,
76            attrs,
77            pat,
78            init,
79            semi_token,
80        }) => {
81            if let Some(init) = init {
82                if init.diverge.is_some() {
83                    panic!("Unsupported diverge in let statement");
84                }
85                let transformed_init = transform_expr(context, &init.expr);
86                quote! { #(#attrs)* #let_token #pat = #transformed_init #semi_token }
87            } else {
88                quote! { #(#attrs)* #let_token #pat #semi_token }
89            }
90        }
91        other => quote! { #other },
92    }
93}
94
95fn transform_expr(context: &syn::Ident, expr: &Expr) -> proc_macro2::TokenStream {
96    match expr {
97        Expr::Block(ExprBlock { block, .. }) => {
98            let transformed_stmts = transform_block(context, block);
99            quote! { #transformed_stmts }
100        }
101        Expr::Let(ExprLet { pat, expr, .. }) => {
102            let transformed_init = transform_expr(context, expr);
103            quote! { let #pat = #transformed_init; }
104        }
105        Expr::Tuple(ExprTuple { elems, .. }) => {
106            let transformed_elems = elems
107                .into_iter()
108                .map(|arg| transform_expr(context, arg))
109                .collect::<Vec<_>>();
110            quote! { (#(#transformed_elems),*) }
111        }
112        Expr::Assign(ExprAssign { left, right, .. }) => {
113            let transformed_right = transform_expr(context, right);
114            match &**left {
115                // Handle simple variable assignments like A = expr
116                Expr::Path(path_expr) => {
117                    if let Some(ident) = path_expr.path.get_ident() {
118                        match ident.to_string().as_str() {
119                            "A" | "B" | "PC" | "DPTR" | "C" | "OV" | "AC" | "Z" => {
120                                quote! { op_def_write!(#context, #ident, #transformed_right); }
121                            }
122                            _ => {
123                                let left_expr = left;
124                                quote! { #left_expr = #transformed_right; }
125                            }
126                        }
127                    } else {
128                        let left_expr = left;
129                        quote! { #left_expr = #transformed_right; }
130                    }
131                }
132                // Handle indexed assignments like DATA[index] = expr
133                Expr::Index(index_expr) => {
134                    let transformed_index = transform_expr(context, &index_expr.index);
135
136                    if let Expr::Path(path_expr) = &*index_expr.expr
137                        && let Some(ident) = path_expr.path.get_ident()
138                    {
139                        match ident.to_string().as_str() {
140                            "BIT" | "PBIT" | "CODE" | "XDATA" | "DATA" | "IDATA" | "PDATA"
141                            | "R" => {
142                                return quote! {{
143                                    let index = #transformed_index;
144                                    let value = #transformed_right;
145                                    op_def_write!(#context, #ident, index, value);
146                                }};
147                            }
148                            _ => {}
149                        }
150                    }
151
152                    let left_expr = transform_expr(context, left);
153                    quote! { #left_expr = #transformed_right; }
154                }
155                Expr::Tuple(ExprTuple { elems, .. }) => {
156                    let tmp = syn::Ident::new("tmp", Span::call_site());
157                    let transformed_elems = elems
158                        .into_iter()
159                        .enumerate()
160                        .map(|(i, left)| {
161                            transform_expr(
162                                context,
163                                &Expr::Assign(ExprAssign {
164                                    attrs: vec![],
165                                    left: Box::new(left.clone()),
166                                    eq_token: Token![=](Span::call_site()),
167                                    right: Box::new(Expr::Field(ExprField {
168                                        attrs: vec![],
169                                        base: Box::new(Expr::Path(ExprPath {
170                                            attrs: vec![],
171                                            qself: None,
172                                            path: ident_to_path(&tmp),
173                                        })),
174                                        dot_token: Token![.](Span::call_site()),
175                                        member: syn::Member::Unnamed(syn::Index::from(i)),
176                                    })),
177                                }),
178                            )
179                        })
180                        .collect::<Vec<_>>();
181                    quote! { {
182                        let #tmp = #transformed_right;
183                        #(#transformed_elems;)*
184                    } }
185                }
186                _ => {
187                    panic!("Unsupported left expression");
188                }
189            }
190        }
191        Expr::Unary(ExprUnary { op, expr, .. }) => {
192            let transformed_expr = transform_expr(context, expr);
193            quote! { #op #transformed_expr }
194        }
195        Expr::Binary(ExprBinary {
196            left, op, right, ..
197        }) => {
198            use syn::BinOp::*;
199            match op {
200                AddAssign(..) | SubAssign(..) | MulAssign(..) | DivAssign(..) | RemAssign(..)
201                | ShlAssign(..) | ShrAssign(..) | BitAndAssign(..) | BitOrAssign(..)
202                | BitXorAssign(..) => {
203                    let transformed_left = transform_expr(context, left);
204                    let transformed_right = transform_expr(context, right);
205                    let tmp = syn::Ident::new("tmp", Span::call_site());
206                    let assign = transform_expr(
207                        context,
208                        &Expr::Assign(ExprAssign {
209                            attrs: vec![],
210                            left: left.clone(),
211                            eq_token: Token![=](Span::call_site()),
212                            right: Box::new(Expr::Path(ExprPath {
213                                attrs: vec![],
214                                qself: None,
215                                path: ident_to_path(&tmp),
216                            })),
217                        }),
218                    );
219                    quote! {{
220                        let mut tmp = #transformed_left;
221                        tmp #op #transformed_right;
222                        #assign;
223                    }}
224                }
225                _ => {
226                    let transformed_left = transform_expr(context, left);
227                    let transformed_right = transform_expr(context, right);
228                    quote! { #transformed_left #op #transformed_right }
229                }
230            }
231        }
232        Expr::Call(ExprCall { func, args, .. }) => {
233            let transformed_args = args
234                .into_iter()
235                .map(|arg| transform_expr(context, arg))
236                .collect::<Vec<_>>();
237
238            if let Expr::Path(ExprPath { path, .. }) = &**func {
239                let ident = path.get_ident().unwrap();
240                match ident.to_string().as_str() {
241                    "POP" | "POP16" | "PUSH" | "PUSH16" | "CLEAR_INT" | "SEXT" => {
242                        return quote! { op_def_call!(#context, #ident (#(#transformed_args),*)) };
243                    }
244                    _ => {}
245                }
246            }
247
248            let transformed_func = transform_expr(context, func);
249            quote! { #transformed_func(#(#transformed_args),*) }
250        }
251        Expr::Paren(ExprParen { expr, .. }) => {
252            let transformed = transform_expr(context, expr);
253            quote! { (#transformed) }
254        }
255        Expr::MethodCall(ExprMethodCall {
256            receiver,
257            method,
258            args,
259            turbofish,
260            ..
261        }) => {
262            if turbofish.is_some() {
263                panic!("Turbofish not supported");
264            }
265            let transformed_expr = transform_expr(context, receiver);
266            let transformed_args = args
267                .into_iter()
268                .map(|arg| transform_expr(context, arg))
269                .collect::<Vec<_>>();
270            quote! { #transformed_expr.#method(#(#transformed_args),*) }
271        }
272        Expr::Field(ExprField { base, member, .. }) => {
273            let transformed_base = transform_expr(context, base);
274            quote! { #transformed_base.#member }
275        }
276        Expr::Path(path_expr) => {
277            if let Some(ident) = path_expr.path.get_ident() {
278                match ident.to_string().as_str() {
279                    "A" | "B" | "PC" | "DPTR" | "C" | "OV" | "AC" | "Z" => {
280                        quote! { op_def_read!(#context, #ident) }
281                    }
282                    _ => {
283                        let expr_ref = &expr;
284                        quote! { #expr_ref }
285                    }
286                }
287            } else {
288                let expr_ref = &expr;
289                quote! { #expr_ref }
290            }
291        }
292        Expr::Index(index_expr) => {
293            let transformed_index = transform_expr(context, &index_expr.index);
294
295            match &*index_expr.expr {
296                Expr::Path(path_expr) => {
297                    if let Some(ident) = path_expr.path.get_ident() {
298                        match ident.to_string().as_str() {
299                            "BIT" | "PBIT" | "CODE" | "XDATA" | "DATA" | "IDATA" | "PDATA"
300                            | "R" => {
301                                quote! { op_def_read!(#context, #ident, #transformed_index) }
302                            }
303                            _ => {
304                                let expr_ref = &expr;
305                                quote! { #expr_ref }
306                            }
307                        }
308                    } else {
309                        let expr_ref = &expr;
310                        quote! { #expr_ref }
311                    }
312                }
313                _ => {
314                    let expr_ref = &expr;
315                    quote! { #expr_ref }
316                }
317            }
318        }
319        Expr::If(ExprIf {
320            cond,
321            then_branch,
322            else_branch,
323            ..
324        }) => {
325            let transformed_cond = transform_expr(context, cond);
326            let transformed_then = transform_block(context, then_branch);
327            if let Some((_, else_branch)) = else_branch {
328                let transformed_otherwise = transform_expr(context, else_branch);
329                quote! { if #transformed_cond #transformed_then else #transformed_otherwise }
330            } else {
331                quote! { if #transformed_cond #transformed_then }
332            }
333        }
334        Expr::Lit(ExprLit { lit, .. }) => {
335            quote! { #lit }
336        }
337        _other => panic!("Unsupported expression"),
338    }
339}
340
341fn ident_to_path(ident: &syn::Ident) -> Path {
342    Path {
343        leading_colon: None,
344        segments: Punctuated::from_iter(vec![PathSegment {
345            ident: ident.clone(),
346            arguments: PathArguments::None,
347        }]),
348    }
349}