1use essential_asm_spec::{visit, Group, Node, Op, StackOut, Tree};
7use proc_macro::TokenStream;
8use proc_macro2::Span;
9use quote::ToTokens;
10use syn::{punctuated::Punctuated, token::Comma};
11
12const WORD_SIZE: usize = core::mem::size_of::<essential_types::Word>();
13
14fn bytecode_arg_docs(num_arg_bytes: u8) -> String {
16 if num_arg_bytes == 0 {
17 return String::new();
18 }
19 assert_eq!(
20 num_arg_bytes as usize % WORD_SIZE,
21 0,
22 "doc gen currently only supports arguments that are a multiple of the word size"
23 );
24 let arg_words = num_arg_bytes as usize / WORD_SIZE;
25 format!(
26 "## Bytecode Argument\nThis operation expects a {num_arg_bytes}-byte \
27 ({arg_words}-word) argument following its opcode within bytecode."
28 )
29}
30
31fn stack_in_docs(stack_in: &[String]) -> String {
33 if stack_in.is_empty() {
34 return String::new();
35 }
36 format!("## Stack Input\n`[{}]`\n", stack_in.join(", "))
37}
38
39fn stack_out_docs(stack_out: &StackOut) -> String {
41 match stack_out {
42 StackOut::Fixed(words) if words.is_empty() => String::new(),
43 StackOut::Fixed(words) => {
44 format!("## Stack Output\n`[{}]`\n", words.join(", "))
45 }
46 StackOut::Dynamic(out) => {
47 format!(
48 "## Stack Output\n`[{}, ...]`\nThe stack output length depends on the \
49 value of the `{}` stack input word.\n",
50 out.elem, out.len
51 )
52 }
53 }
54}
55
56fn panic_docs(panic_reasons: &[String]) -> String {
58 if panic_reasons.is_empty() {
59 return String::new();
60 }
61 let mut docs = "## Panics\n".to_string();
62 panic_reasons
63 .iter()
64 .for_each(|reason| docs.push_str(&format!("- {reason}\n")));
65 docs
66}
67
68fn op_docs(op: &Op) -> String {
70 let arg_docs = bytecode_arg_docs(op.num_arg_bytes);
71 let short_docs = if op.short.is_empty() {
72 String::new()
73 } else {
74 format!(": `{}`", op.short)
75 };
76 let opcode_docs = format!("`0x{:02X}`{}\n\n", op.opcode, short_docs);
77 let desc = &op.description;
78 let stack_in_docs = stack_in_docs(&op.stack_in);
79 let stack_out_docs = stack_out_docs(&op.stack_out);
80 let panic_docs = panic_docs(&op.panics);
81 format!("{opcode_docs}\n{desc}\n{arg_docs}\n{stack_in_docs}\n{stack_out_docs}\n{panic_docs}")
82}
83
84fn opcode_docs(enum_name: &str, name: &str, op: &Op) -> String {
86 let opcode_docs = format!("`0x{:02X}`\n\n", op.opcode);
87 let docs = format!(
88 "Opcode associated with the \
89 [{enum_name}::{name}][super::op::{enum_name}::{name}] operation."
90 );
91 format!("{opcode_docs}\n{docs}")
92}
93
94fn op_enum_decl_variant(name: &str, node: &Node) -> syn::Variant {
96 let ident = syn::Ident::new(name, Span::call_site());
97 match node {
98 Node::Group(group) => {
99 let docs = &group.description;
100 syn::parse_quote! {
101 #[doc = #docs]
102 #ident(#ident)
103 }
104 }
105 Node::Op(op) => {
106 let docs = op_docs(op);
107 match op.num_arg_bytes {
108 0 => syn::parse_quote! {
109 #[doc = #docs]
110 #ident
111 },
112 8 => syn::parse_quote! {
113 #[doc = #docs]
114 #ident(essential_types::Word)
115 },
116 _ => panic!(
117 "Unexpected num_arg_bytes {}: requires more thoughtful asm-gen",
118 op.num_arg_bytes
119 ),
120 }
121 }
122 }
123}
124
125fn opcode_enum_decl_variant(parent_name: &str, name: &str, node: &Node) -> syn::Variant {
127 let ident = syn::Ident::new(name, Span::call_site());
128 match node {
129 Node::Group(group) => {
130 let docs = &group.description;
131 syn::parse_quote! {
132 #[doc = #docs]
133 #ident(#ident)
134 }
135 }
136 Node::Op(op) => {
137 let docs = opcode_docs(parent_name, name, op);
138 let opcode = op.opcode;
139 syn::parse_quote! {
140 #[doc = #docs]
141 #ident = #opcode
142 }
143 }
144 }
145}
146
147fn op_enum_decl_variants(group: &Group) -> Punctuated<syn::Variant, Comma> {
149 group
150 .tree
151 .iter()
152 .map(|(name, node)| op_enum_decl_variant(name, node))
153 .collect()
154}
155
156fn opcode_enum_decl_variants(enum_name: &str, group: &Group) -> Punctuated<syn::Variant, Comma> {
158 group
159 .tree
160 .iter()
161 .map(|(name, node)| opcode_enum_decl_variant(enum_name, name, node))
162 .collect()
163}
164
165fn op_enum_decl(name: &str, group: &Group) -> syn::ItemEnum {
167 let variants = op_enum_decl_variants(group);
168 let ident = syn::Ident::new(name, Span::call_site());
169 let docs = &group.description;
170 let item_enum = syn::parse_quote! {
171 #[doc = #docs]
172 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
173 pub enum #ident {
174 #variants
175 }
176 };
177 item_enum
178}
179
180fn opcode_enum_decl(name: &str, group: &Group) -> syn::ItemEnum {
182 let variants = opcode_enum_decl_variants(name, group);
183 let ident = syn::Ident::new(name, Span::call_site());
184 let docs = &group.description;
185 let item_enum = syn::parse_quote! {
186 #[doc = #docs]
187 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
188 #[repr(u8)]
189 pub enum #ident {
190 #variants
191 }
192 };
193 item_enum
194}
195
196fn op_enum_impl_opcode_arms(enum_ident: &syn::Ident, group: &Group) -> Vec<syn::Arm> {
198 group
199 .tree
200 .iter()
201 .map(|(name, node)| {
202 let name = syn::Ident::new(name, Span::call_site());
203 match node {
204 Node::Group(_) => syn::parse_quote! {
205 #enum_ident::#name(group) => crate::opcode::#enum_ident::#name(group.to_opcode()),
206 },
207 Node::Op(op) if op.num_arg_bytes > 0 => {
208 syn::parse_quote! {
209 #enum_ident::#name(_) => crate::opcode::#enum_ident::#name,
210 }
211 }
212 Node::Op(_) => {
213 syn::parse_quote! {
214 #enum_ident::#name => crate::opcode::#enum_ident::#name,
215 }
216 }
217 }
218 })
219 .collect()
220}
221
222fn op_enum_impl_opcode(name: &str, group: &Group) -> syn::ItemImpl {
224 let name = syn::Ident::new(name, Span::call_site());
225 let arms = op_enum_impl_opcode_arms(&name, group);
226 syn::parse_quote! {
227 impl ToOpcode for #name {
228 type Opcode = crate::opcode::#name;
229 fn to_opcode(&self) -> Self::Opcode {
230 match *self {
231 #(
232 #arms
233 )*
234 }
235 }
236 }
237 }
238}
239
240fn op_enum_impl_try_from_bytes(name: &str) -> syn::ItemImpl {
243 let name = syn::Ident::new(name, Span::call_site());
244 syn::parse_quote! {
245 impl TryFromBytes for #name {
246 type Error = crate::FromBytesError;
247 fn try_from_bytes(
248 bytes: &mut impl Iterator<Item = u8>,
249 ) -> Option<Result<Self, Self::Error>> {
250 use crate::opcode::ParseOp;
251 let opcode_byte = bytes.next()?;
252 let op_res = crate::opcode::#name::try_from(opcode_byte)
253 .map_err(From::from)
254 .and_then(|opcode| opcode.parse_op(bytes).map_err(From::from));
255 Some(op_res)
256 }
257 }
258 }
259}
260
261fn op_enum_bytes_iter_decl_variant(name: &str, node: &Node) -> syn::Variant {
263 let ident = syn::Ident::new(name, Span::call_site());
264 match node {
265 Node::Group(_group) => {
266 syn::parse_quote! {
267 #ident(#ident)
268 }
269 }
270 Node::Op(op) => {
271 let n_bytes: usize = 1 + op.num_arg_bytes as usize;
273 let n_bytes: syn::LitInt = syn::parse_quote!(#n_bytes);
274 syn::parse_quote! {
275 #ident {
276 index: usize,
278 bytes: [u8; #n_bytes],
280 }
281 }
282 }
283 }
284}
285
286fn op_enum_bytes_iter_decl_variants(group: &Group) -> Vec<syn::Variant> {
288 group
289 .tree
290 .iter()
291 .map(|(name, node)| op_enum_bytes_iter_decl_variant(name, node))
292 .collect()
293}
294
295fn op_enum_bytes_iter_decl(name: &str, group: &Group) -> syn::ItemEnum {
297 let name = syn::Ident::new(name, Span::call_site());
298 let variants = op_enum_bytes_iter_decl_variants(group);
299 let docs = format!(
300 "The bytes iterator produced by the \
301 [{name}::to_bytes][super::ToBytes::to_bytes] method."
302 );
303 syn::parse_quote! {
304 #[doc = #docs]
305 #[derive(Clone, Debug)]
306 pub enum #name {
307 #(
308 #variants
310 ),*
311 }
312 }
313}
314
315fn op_enum_bytes_iter_impl_arm(name: &str, node: &Node) -> syn::Arm {
317 let name = syn::Ident::new(name, Span::call_site());
318 match node {
319 Node::Group(_group) => syn::parse_quote! {
320 Self::#name(ref mut iter) => iter.next(),
321 },
322 Node::Op(_op) => {
323 syn::parse_quote! {
324 Self::#name { ref mut index, ref bytes } => {
325 let byte = *bytes.get(*index)?;
326 *index += 1;
327 Some(byte)
328 },
329 }
330 }
331 }
332}
333
334fn op_enum_bytes_iter_impl_arms(group: &Group) -> Vec<syn::Arm> {
336 group
337 .tree
338 .iter()
339 .map(|(name, node)| op_enum_bytes_iter_impl_arm(name, node))
340 .collect()
341}
342
343fn op_enum_bytes_iter_impl(name: &str, group: &Group) -> syn::ItemImpl {
345 let bytes_iter = syn::Ident::new(name, Span::call_site());
346 let arms = op_enum_bytes_iter_impl_arms(group);
347 syn::parse_quote! {
348 impl Iterator for #bytes_iter {
349 type Item = u8;
350 fn next(&mut self) -> Option<Self::Item> {
351 match *self {
352 #(
353 #arms
354 )*
355 }
356 }
357 }
358 }
359}
360
361fn op_enum_impl_to_bytes_arm(enum_name: &syn::Ident, name: &str, node: &Node) -> syn::Arm {
363 let name = syn::Ident::new(name, Span::call_site());
364 match node {
365 Node::Group(_group) => {
366 syn::parse_quote! {
367 Self::#name(group) => bytes_iter::#enum_name::#name(group.to_bytes()),
368 }
369 }
370 Node::Op(op) => {
371 let opcode = op.opcode;
372 if op.num_arg_bytes == 0 {
373 syn::parse_quote! {
374 Self::#name => bytes_iter::#enum_name::#name {
375 index: 0usize,
376 bytes: [#opcode],
377 },
378 }
379 } else if op.num_arg_bytes == 8 {
380 syn::parse_quote! {
381 Self::#name(data) => {
382 use essential_types::convert::bytes_from_word;
383 let [b0, b1, b2, b3, b4, b5, b6, b7] = bytes_from_word(data.clone());
384 bytes_iter::#enum_name::#name {
385 index: 0usize,
386 bytes: [#opcode, b0, b1, b2, b3, b4, b5, b6, b7],
387 }
388 },
389 }
390 } else {
391 panic!(
392 "Currently only support operations with a single word \
393 argument. This must be updated to properly support variable \
394 size arguments.",
395 )
396 }
397 }
398 }
399}
400
401fn op_enum_impl_to_bytes_arms(enum_name: &syn::Ident, group: &Group) -> Vec<syn::Arm> {
403 group
404 .tree
405 .iter()
406 .map(|(name, node)| op_enum_impl_to_bytes_arm(enum_name, name, node))
407 .collect()
408}
409
410fn op_enum_impl_to_bytes(name: &str, group: &Group) -> syn::ItemImpl {
412 let name = syn::Ident::new(name, Span::call_site());
413 let arms = op_enum_impl_to_bytes_arms(&name, group);
414 syn::parse_quote! {
415 impl ToBytes for #name {
416 type Bytes = bytes_iter::#name;
417 fn to_bytes(&self) -> Self::Bytes {
418 match self {
419 #(
420 #arms
421 )*
422 }
423 }
424 }
425 }
426}
427
428fn impl_from_subgroup(names: &[String]) -> syn::ItemImpl {
431 let ident = syn::Ident::new(names.first().unwrap(), Span::call_site());
432 let subident = syn::Ident::new(names.last().unwrap(), Span::call_site());
433 let inner_expr: syn::Expr = syn::parse_quote!(subgroup);
434 let expr = enum_variant_tuple1_expr(names, inner_expr);
435 syn::parse_quote! {
436 impl From<#subident> for #ident {
437 fn from(subgroup: #subident) -> Self {
438 #expr
439 }
440 }
441 }
442}
443
444fn impl_from_subgroups(name: &str, group: &Group) -> Vec<syn::ItemImpl> {
446 let mut impls = vec![];
447 let mut names = vec![name.to_string()];
448 visit::groups_filtered_recurse(&group.tree, &|_| true, &mut names, &mut |names, _group| {
449 impls.push(impl_from_subgroup(names));
450 });
451 impls
452}
453
454fn enum_variant_tuple1_expr(names: &[String], mut expr: syn::Expr) -> syn::Expr {
458 assert!(!names.is_empty(), "Expecting at least one variant name");
459 let mut idents: Vec<_> = names
460 .iter()
461 .map(|n| syn::Ident::new(n, Span::call_site()))
462 .collect();
463 let mut variant_name = idents.pop().unwrap();
464 while let Some(enum_name) = idents.pop() {
465 expr = syn::parse_quote!(#enum_name::#variant_name(#expr));
466 variant_name = enum_name;
467 }
468 expr
469}
470
471fn opcode_expr_from_names(names: &[String]) -> syn::Expr {
474 assert!(
475 names.len() >= 2,
476 "Expecting at least the enum and variant names"
477 );
478 let enum_name = syn::Ident::new(&names[names.len() - 2], Span::call_site());
479 let variant_name = syn::Ident::new(&names[names.len() - 1], Span::call_site());
480 let expr = syn::parse_quote!(#enum_name::#variant_name);
481 enum_variant_tuple1_expr(&names[..names.len() - 1], expr)
482}
483
484fn opcode_enum_impl_parse_op_arm(
486 enum_name: &syn::Ident,
487 name: &syn::Ident,
488 node: &Node,
489) -> syn::Arm {
490 match node {
491 Node::Group(_group) => {
492 syn::parse_quote! {
493 Self::#name(group) => group.parse_op(bytes).map(Into::into),
494 }
495 }
496 Node::Op(op) if op.num_arg_bytes == 0 => {
497 syn::parse_quote! {
498 Self::#name => Ok(crate::op::#enum_name::#name),
499 }
500 }
501 Node::Op(op) => {
504 assert_eq!(
505 op.num_arg_bytes as usize % WORD_SIZE,
506 0,
507 "Currently only support operations with an `num_arg_bytes` that is \
508 a multiple of the word size",
509 );
510 let words = op.num_arg_bytes as usize / WORD_SIZE;
511 assert_eq!(
512 words, 1,
513 "Currently only support operations with a single word \
514 argument. This must be updated to properly support variable \
515 size arguments.",
516 );
517 syn::parse_quote! {
518 Self::#name => {
519 use essential_types::convert::word_from_bytes;
520 fn parse_word_bytes(bytes: &mut impl Iterator<Item = u8>) -> Option<[u8; 8]> {
521 Some([
522 bytes.next()?, bytes.next()?, bytes.next()?, bytes.next()?,
523 bytes.next()?, bytes.next()?, bytes.next()?, bytes.next()?,
524 ])
525 }
526 let word_bytes: [u8; 8] = parse_word_bytes(bytes).ok_or(NotEnoughBytesError)?;
527 let word: essential_types::Word = word_from_bytes(word_bytes);
528 Ok(crate::op::#enum_name::#name(word))
529 },
530 }
531 }
532 }
533}
534
535fn opcode_enum_impl_parse_op_arms(enum_name: &syn::Ident, group: &Group) -> Vec<syn::Arm> {
537 group
538 .tree
539 .iter()
540 .map(|(name, node)| {
541 let name = syn::Ident::new(name, Span::call_site());
542 opcode_enum_impl_parse_op_arm(enum_name, &name, node)
543 })
544 .collect()
545}
546
547fn opcode_enum_impl_parse_op(name: &str, group: &Group) -> syn::ItemImpl {
549 let ident = syn::Ident::new(name, Span::call_site());
550 let arms = opcode_enum_impl_parse_op_arms(&ident, group);
551 syn::parse_quote! {
552 impl ParseOp for #ident {
553 type Op = crate::op::#ident;
554 type Error = NotEnoughBytesError;
555
556 fn parse_op(
563 &self,
564 bytes: &mut impl Iterator<Item = u8>,
565 ) -> Result<Self::Op, Self::Error> {
566 match *self {
567 #(
568 #arms
569 )*
570 }
571 }
572 }
573 }
574}
575
576fn opcode_enum_impl_tryfrom_u8_arms(group: &Group) -> Vec<syn::Arm> {
578 let mut arms = vec![];
579 let mut names = vec!["Self".to_string()];
580 visit::ops_filtered_recurse(&group.tree, &|_| true, &mut names, &mut |names, op| {
581 let opcode = op.opcode;
582 let opcode_expr = opcode_expr_from_names(names);
583 let arm = syn::parse_quote! {
584 #opcode => #opcode_expr,
585 };
586 arms.push(arm);
587 });
588 arms
589}
590
591fn opcode_enum_impl_from_opcode_for_u8_arms(
593 enum_ident: &syn::Ident,
594 group: &Group,
595) -> Vec<syn::Arm> {
596 group
597 .tree
598 .iter()
599 .map(|(name, node)| {
600 let name = syn::Ident::new(name, Span::call_site());
601 match node {
602 Node::Group(_) => syn::parse_quote! {
603 #enum_ident::#name(group) => u8::from(group),
604 },
605 Node::Op(op) => {
606 let opcode = op.opcode;
607 syn::parse_quote! {
608 #enum_ident::#name => #opcode,
609 }
610 }
611 }
612 })
613 .collect()
614}
615
616fn opcode_enum_impl_from_opcode_for_u8(name: &str, group: &Group) -> syn::ItemImpl {
618 let name = syn::Ident::new(name, Span::call_site());
619 let arms = opcode_enum_impl_from_opcode_for_u8_arms(&name, group);
620 syn::parse_quote! {
621 impl From<#name> for u8 {
622 fn from(opcode: #name) -> Self {
623 match opcode {
624 #(
625 #arms
626 )*
627 }
628 }
629 }
630 }
631}
632
633fn opcode_enum_impl_tryfrom_u8(name: &str, group: &Group) -> syn::ItemImpl {
635 let name = syn::Ident::new(name, Span::call_site());
636 let arms = opcode_enum_impl_tryfrom_u8_arms(group);
637 syn::parse_quote! {
638 impl TryFrom<u8> for #name {
639 type Error = InvalidOpcodeError;
640 fn try_from(u: u8) -> Result<Self, Self::Error> {
641 let opcode = match u {
642 #(
643 #arms
644 )*
645 _ => return Err(InvalidOpcodeError(u)),
646 };
647 Ok(opcode)
648 }
649 }
650 }
651}
652
653fn op_enum_impls(names: &[String], group: &Group) -> Vec<syn::ItemImpl> {
655 let name = names.last().unwrap();
656 let mut impls = vec![
657 op_enum_impl_opcode(name, group),
658 op_enum_impl_to_bytes(name, group),
659 op_enum_impl_try_from_bytes(name),
660 ];
661 impls.extend(impl_from_subgroups(name, group));
662 impls
663}
664
665fn op_const_expr(names: &[String], insert_word: bool) -> syn::Expr {
667 let last = syn::Ident::new(names.last().unwrap(), Span::call_site());
668 let last: syn::Expr = if insert_word {
669 syn::parse_quote!(#last(word))
670 } else {
671 syn::parse_quote!(#last)
672 };
673 names[1..(names.len() - 1)].iter().rev().fold(
674 syn::parse_quote!(#last),
675 |acc: syn::Expr, name| {
676 let name = syn::Ident::new(name, Span::call_site());
677 syn::parse_quote!(#name(#name::#acc))
678 },
679 )
680}
681
682fn op_consts(names: &[String], op: &Op) -> Vec<syn::Item> {
684 let const_name = if op.short.is_empty() {
685 syn::Ident::new(&names.last().unwrap().to_uppercase(), Span::call_site())
686 } else {
687 syn::Ident::new(&op.short, Span::call_site())
688 };
689 let docs = format!("## {}\n\n{}", names.last().unwrap(), op_docs(op));
690
691 if op.num_arg_bytes == 0 {
692 let full_name = op_const_expr(names, false);
693 let s = syn::parse_quote! {
694 #[doc = #docs]
695 pub const #const_name: Op = Op::#full_name;
696 };
697 vec![syn::Item::Const(s)]
698 } else {
699 let full_name = op_const_expr(names, true);
700 let mod_name = syn::Ident::new(&names.last().unwrap().to_lowercase(), Span::call_site());
701 let mut v = vec![];
702 let s = syn::parse_quote! {
703 #[doc = #docs]
704 pub const #const_name: fn(i64) -> Op = #mod_name::#mod_name;
705 };
706 v.push(syn::Item::Const(s));
707 let s = syn::parse_quote! {
708 mod #mod_name {
709 use super::*;
710 pub(super) fn #mod_name(word: i64) -> Op {
711 Op::#full_name
712 }
713
714 }
715 };
716 v.push(syn::Item::Mod(s));
717 v
718 }
719}
720
721fn opcode_enum_impls(names: &[String], group: &Group) -> Vec<syn::ItemImpl> {
723 let name = names.last().unwrap();
724 let mut impls = vec![
725 opcode_enum_impl_from_opcode_for_u8(name, group),
726 opcode_enum_impl_tryfrom_u8(name, group),
727 opcode_enum_impl_parse_op(name, group),
728 ];
729 impls.extend(impl_from_subgroups(name, group));
730 impls
731}
732
733fn all_op_consts(tree: &Tree) -> Vec<syn::Item> {
735 let mut items = vec![];
736 visit::ops(tree, &mut |names, op| {
737 items.extend(op_consts(names, op));
738 });
739 items
740}
741
742fn collect_items(tree: &Tree, new_item: impl Fn(&[String], &Group) -> syn::Item) -> Vec<syn::Item> {
744 let mut items = vec![];
745 visit::groups(tree, &mut |str, group| items.push(new_item(str, group)));
746 items
747}
748
749fn all_op_enum_decls(tree: &Tree) -> Vec<syn::Item> {
751 collect_items(tree, |names, group| {
752 let name = names.last().unwrap();
753 syn::Item::Enum(op_enum_decl(name, group))
754 })
755}
756
757fn all_opcode_enum_decls(tree: &Tree) -> Vec<syn::Item> {
759 collect_items(tree, |names, group| {
760 let name = names.last().unwrap();
761 syn::Item::Enum(opcode_enum_decl(name, group))
762 })
763}
764
765fn all_op_enum_bytes_iter(tree: &Tree) -> Vec<syn::Item> {
767 let mut items = vec![];
768 visit::groups(tree, &mut |names, group| {
769 let name = names.last().unwrap();
770 items.push(syn::Item::Enum(op_enum_bytes_iter_decl(name, group)));
771 items.push(syn::Item::Impl(op_enum_bytes_iter_impl(name, group)));
772 });
773 items
774}
775
776fn all_op_enum_impls(tree: &Tree) -> Vec<syn::Item> {
778 let mut items = vec![];
779 visit::groups(tree, &mut |name, group| {
780 items.extend(op_enum_impls(name, group).into_iter().map(syn::Item::Impl));
781 });
782 items
783}
784
785fn all_opcode_enum_impls(tree: &Tree) -> Vec<syn::Item> {
787 let mut items = vec![];
788 visit::groups(tree, &mut |name, group| {
789 items.extend(
790 opcode_enum_impls(name, group)
791 .into_iter()
792 .map(syn::Item::Impl),
793 );
794 });
795 items
796}
797
798const DOCS_TABLE_HEADER: &str = "\n\n\
799 | Opcode | Op | Short Description |\n\
800 | --- | --- | --- |\n";
801
802fn docs_table_row(names: &[String], op: &Op) -> String {
804 assert!(
805 names.len() >= 2,
806 "`names` should contain at least the group and op names"
807 );
808 let enum_ix = names.len() - 2;
809 let enum_variant = &names[enum_ix..];
810 let enum_name = enum_variant.first().unwrap();
811 let variant_name = enum_variant.last().unwrap();
812 let opcode_link = format!("opcode::{enum_name}::{variant_name}");
813 let op_link = format!("op::{enum_name}::{variant_name}");
814 let short_desc = op.description.lines().next().unwrap();
815 format!(
816 "| [`0x{:02X}`][{opcode_link}] | [{}][{op_link}] | {short_desc} |\n",
817 op.opcode,
818 enum_variant.join(" ")
819 )
820}
821
822fn ops_docs_table(tree: &Tree) -> syn::LitStr {
824 let mut docs = DOCS_TABLE_HEADER.to_string();
825 visit::ops(tree, &mut |names, op| {
826 docs.push_str(&docs_table_row(names, op));
827 });
828 syn::parse_quote! { #docs }
829}
830
831fn token_stream_from_items(items: impl IntoIterator<Item = syn::Item>) -> TokenStream {
832 items
833 .into_iter()
834 .flat_map(|item| item.into_token_stream())
835 .collect::<proc_macro2::TokenStream>()
836 .into()
837}
838
839#[proc_macro]
840pub fn gen_all_op_decls(_input: TokenStream) -> TokenStream {
841 let tree = essential_asm_spec::tree();
842 let items = all_op_enum_decls(&tree);
843 token_stream_from_items(items)
844}
845
846#[proc_macro]
847pub fn gen_all_opcode_decls(_input: TokenStream) -> TokenStream {
848 let tree = essential_asm_spec::tree();
849 let items = all_opcode_enum_decls(&tree);
850 token_stream_from_items(items)
851}
852
853#[proc_macro]
854pub fn gen_all_op_bytes_iter(_input: TokenStream) -> TokenStream {
855 let tree = essential_asm_spec::tree();
856 let items = all_op_enum_bytes_iter(&tree);
857 token_stream_from_items(items)
858}
859
860#[proc_macro]
861pub fn gen_all_op_impls(_input: TokenStream) -> TokenStream {
862 let tree = essential_asm_spec::tree();
863 let items = all_op_enum_impls(&tree);
864 token_stream_from_items(items)
865}
866
867#[proc_macro]
868pub fn gen_all_opcode_impls(_input: TokenStream) -> TokenStream {
869 let tree = essential_asm_spec::tree();
870 let items = all_opcode_enum_impls(&tree);
871 token_stream_from_items(items)
872}
873
874#[proc_macro]
875pub fn gen_ops_docs_table(_input: TokenStream) -> TokenStream {
876 let tree = essential_asm_spec::tree();
877 let lit_str = ops_docs_table(&tree);
878 lit_str.into_token_stream().into()
879}
880
881#[proc_macro]
882pub fn gen_all_op_consts(_input: TokenStream) -> TokenStream {
883 let tree = essential_asm_spec::tree();
884 let items = all_op_consts(&tree);
885
886 token_stream_from_items(items)
887}