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#[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#[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 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 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),
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),
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),
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}