From f3aa97b365374c624cb20b309b230e1218284df9 Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Sat, 14 Aug 2021 08:40:36 +0200 Subject: [PATCH] Opcode table via procedural macro --- Cargo.lock | 44 +++++ Cargo.toml | 1 + lib/Cargo.toml | 1 + lib/src/lib.rs | 452 +++++++++++++++++++++++----------------------- macros/Cargo.toml | 12 ++ macros/src/lib.rs | 144 +++++++++++++++ 6 files changed, 427 insertions(+), 227 deletions(-) create mode 100644 macros/Cargo.toml create mode 100644 macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 0d95243..1cb9fdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,7 @@ name = "ppc750cl" version = "0.1.0" dependencies = [ "num-traits", + "ppc750cl-macros", ] [[package]] @@ -74,6 +75,14 @@ dependencies = [ "ppc750cl", ] +[[package]] +name = "ppc750cl-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "ppc750cl-rand" version = "0.1.0" @@ -89,6 +98,24 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "proc-macro2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rand" version = "0.7.3" @@ -140,6 +167,23 @@ dependencies = [ "rand_core", ] +[[package]] +name = "syn" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 97a7d77..bc6f917 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "lib", + "macros", "fuzz", "rand", ] diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 421df09..a9d3fb2 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -6,3 +6,4 @@ authors = ["Richard Patel "] [dependencies] num-traits = "0.2.14" +ppc750cl-macros = { path = "../macros" } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index d6895c8..8cb9d1c 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -5,233 +5,231 @@ use std::ops::Range; use num_traits::AsPrimitive; -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum Opcode { - Illegal = -1, - // 750CL extensions - Twi, - PsCmpu0, - PsqLx, - PsqStx, - PsSum0, - PsSum1, - PsMuls0, - PsMuls1, - PsMadds0, - PsMadds1, - PsDiv, - PsSub, - PsAdd, - PsSel, - PsRes, - PsMul, - PsRsqrte, - PsMsub, - PsMadd, - PsNmsub, - PsNmadd, - PsCmpo0, - PsqLux, - PsqStux, - PsNeg, - PsCmpu1, - PsMr, - PsCmpo1, - PsNabs, - PsAbs, - PsMerge00, - PsMerge01, - PsMerge10, - PsMerge11, - DcbzL, - Mulli, - Subfic, - Cmpli, - Cmpi, - Addic, - Addic_, - Addi, - Addis, - Bc, - Sc, - B, - Mcrf, - Bclr, - Crnor, - Rfi, - Crandc, - Isync, - Crxor, - Crnand, - Crand, - Creqv, - Crorc, - Cror, - Bcctr, - Rlwimi, - Rlwinm, - Rlwnm, - Ori, - Oris, - Xori, - Xoris, - Andi_, - Andis_, - Cmp, - Tw, - Subfc, - Addc, - Mulhwu, - Mfcr, - Lwarx, - Lwzx, - Slw, - Cntlzw, - And, - Cmpl, - Subf, - Dcbst, - Lwzux, - Andc, - Mulhw, - Mfmsr, - Dcbf, - Lbzx, - Neg, - Lbzux, - Nor, - Subfe, - Adde, - Mtcrf, - Mtmsr, - Stwcx_, - Stwx, - Stwux, - Subfze, - Addze, - Mtsr, - Stbx, - Subfme, - Addme, - Mullw, - Mtsrin, - Dcbtst, - Stbux, - Add, - Dcbt, - Lhzx, - Eqv, - Tlbie, - Eciwx, - Lhzux, - Xor, - Mfspr, - Lhax, - Mftb, - Lhaux, - Sthx, - Orc, - Ecowx, - Sthux, - Or, - Divwu, - Mtspr, - Dcbi, - Nand, - Divw, - Mcrxr, - Lswx, - Lwbrx, - Lfsx, - Srw, - Tlbsync, - Lfsux, - Mfsr, - Lswi, - Sync, - Lfdx, - Lfdux, - Mfsrin, - Stswx, - Stwbrx, - Stfsx, - Stfsux, - Stswi, - Stfdx, - Stfdux, - Lhbrx, - Sraw, - Srawi, - Eieio, - Sthbrx, - Extsh, - Extsb, - Icbi, - Stfiwx, - Dcbz, - Lwz, - Lwzu, - Lbz, - Lbzu, - Stw, - Stwu, - Stb, - Stbu, - Lhz, - Lhzu, - Lha, - Lhau, - Sth, - Sthu, - Lmw, - Stmw, - Lfs, - Lfsu, - Lfd, - Lfdu, - Stfs, - Stfsu, - Stfd, - Stfdu, - PsqL, - PsqLu, - Fdivs, - Fsubs, - Fadds, - Fres, - Fmuls, - Fmsubs, - Fmadds, - Fnmsubs, - Fnmadds, - PsqSt, - PsqStu, - Fcmpu, - Frsp, - Fctiw, - Fctiwz, - Fdiv, - Fsub, - Fadd, - Fsel, - Fmul, - Frsqrte, - Fmsub, - Fmadd, - Fnmsub, - Fnmadd, - Fcmpo, - Mtfsb1, - Fneg, - Mcrfs, - Mtfsb0, - Fmr, - Mtfsfi, - Fnabs, - Fabs, - Mffs, - Mtfsf, - // Basic instructions +use ppc750cl_macros::isa; + +isa! { + "add" & 0xfc0007fe == 0x7c000214; + "addc" & 0xfc0007fe == 0x7c00002a; + "adde" & 0xfc0007fe == 0x7c000114; + "addi" & 0xfc000000 == 0x38000000; + "addic" & 0xfc000000 == 0x30000000; + "addic." & 0xfc000000 == 0x34000000; + "addis" & 0xfc000000 == 0x3c000000; + "addme" & 0xfc00fbfe == 0x7c0001d4; + "addze" & 0xfc00fbfe == 0x7c000194; + "and" & 0xfc0007fe == 0x7c000038; + "andc" & 0xfc0007fe == 0x7c000078; + "andi." & 0xfc000000 == 0x70000000; + "andis." & 0xfc000000 == 0x74000000; + "b" & 0xfc000000 == 0x48000000; + "bc" & 0xfc000000 == 0x40000000; + "bcctr" & 0xfc00ffff == 0x4c000210; + "bclr" & 0xfc00fffe == 0x4c000020; + "cmp" & 0xfc4007ff == 0x7c000000; + "cmpi" & 0xfc400000 == 0x2c000000; + "cmpl" & 0xfc4007ff == 0x7c000040; + "cmpli" & 0xfc400000 == 0x28000000; + "cntlzw" & 0xfc00fffe == 0x7c000034; + "crand" & 0xfc0007ff == 0x4c000202; + "crandc" & 0xfc0007ff == 0x4c000102; + "creqv" & 0xfc0007ff == 0x4c000242; + "crnand" & 0xfc0007ff == 0x4c0001c2; + "crnor" & 0xfc0007ff == 0x4c000042; + "cror" & 0xfc0007ff == 0x4c000382; + "crorc" & 0xfc0007ff == 0x4c000342; + "crxor" & 0xfc0007ff == 0x4c000182; + "dcbf" & 0xffe007ff == 0x7c0000ac; + "dcbi" & 0xffe007ff == 0x7c0003ac; + "dcbst" & 0xffe007ff == 0x7c00006c; + "dcbt" & 0xffe007ff == 0x7c00022c; + "dcbtst" & 0xffe007ff == 0x7c0001ec; + "dcbz" & 0xffe007ff == 0x7c0007ec; + "dcbz_l" & 0xffe007ff == 0x100007ec; + "divw" & 0xfc0003fe == 0x7c0003d6; + "divwu" & 0xfc0003fe == 0x7c000396; + "eciwx" & 0xfc0003ff == 0x7c00026c; + "ecowx" & 0xfc0003ff == 0x7c00036c; + "eieio" & 0xffffffff == 0x7c0006ac; + "eqv" & 0xfc0003fe == 0x7c000238; + "extsb" & 0xfc00fffe == 0x7c000774; + "extsh" & 0xfc00fffe == 0x7c000734; + "fabs" & 0xfc1f07fe == 0xfc000734; + "fadd" & 0xfc0007fe == 0xfc00002a; + "fadds" & 0xfc0007fe == 0xec00002a; + "fcmpo" & 0xfc6007ff == 0xfc000040; + "fcmpu" & 0xfc6007ff == 0xfc000000; + "fctiw" & 0xfc1f07fe == 0xfc00001c; + "fctiwz" & 0xfc1f07fe == 0xfc00001e; + "fdiv" & 0xfc0007fe == 0xfc000024; + "fdivs" & 0xfc0007fe == 0xec000024; + "fmadd" & 0xfc00003e == 0xfc00003a; + "fmadds" & 0xfc00003e == 0xec00003a; + "fmr" & 0xfc1f07fe == 0xfc000090; + "fmsub" & 0xfc00003e == 0xfc000038; + "fmsubs" & 0xfc00003e == 0xec000038; + "fmul" & 0xfc00f83e == 0xfc000032; + "fmuls" & 0xfc00f83e == 0xec000032; + "fnabs" & 0xfc1f07fe == 0xfc000110; + "fneg" & 0xfc1f07fe == 0xfc000050; + "fnmadd" & 0xfc00003e == 0xfc00003e; + "fnmadds" & 0xfc00003e == 0xec00003e; + "fnmsub" & 0xfc00003e == 0xfc00003c; + "fnmsubs" & 0xfc00003e == 0xec00003c; + "fres" & 0xfc1f07fe == 0xec000030; + "frsp" & 0xfc1f07fe == 0xfc000018; + "frsqrte" & 0xfc1f07fe == 0xfc000034; + "fsel" & 0xfc00003e == 0xfc00002e; + "fsub" & 0xfc0007fe == 0xfc000028; + "fsubs" & 0xfc0007fe == 0xec000028; + "icbi" & 0xffe007ff == 0x7c0007ac; + "isync" & 0xffffffff == 0x4c00012c; + "lbz" & 0xfc000000 == 0x88000000; + "lbzu" & 0xfc000000 == 0x8c000000; + "lbzux" & 0xfc0007ff == 0x7c0000ee; + "lbzx" & 0xfc0007ff == 0x7c0000ae; + "lfd" & 0xfc000000 == 0xc8000000; + "lfdu" & 0xfc000000 == 0xcc000000; + "lfdux" & 0xfc0007ff == 0x7c0004ee; + "lfdx" & 0xfc0007ff == 0x7c00045e; + "lfs" & 0xfc000000 == 0xc0000000; + "lfsu" & 0xfc000000 == 0xc4000000; + "lfsux" & 0xfc0007ff == 0x7c00046e; + "lfsx" & 0xfc0007ff == 0x7c00042e; + "lha" & 0xfc000000 == 0xa8000000; + "lhau" & 0xfc000000 == 0xac000000; + "lhaux" & 0xfc0007ff == 0x7c0002ee; + "lhax" & 0xfc0007ff == 0x7c0002ae; + "lhbrx" & 0xfc0007ff == 0x7c00062c; + "lhz" & 0xfc000000 == 0xa0000000; + "lhzu" & 0xfc000000 == 0xa4000000; + "lhzux" & 0xfc0007ff == 0x7c00026e; + "lhzx" & 0xfc0007ff == 0x7c00022e; + "lmw" & 0xfc000000 == 0xb8000000; + "lswi" & 0xfc0007ff == 0x7c0004aa; + "lswx" & 0xfc0007ff == 0x7c00042a; + "lwarx" & 0xfc0007ff == 0x7c000028; + "lwbrx" & 0xfc0007ff == 0x7c00042c; + "lwz" & 0xfc000000 == 0x80000000; + "lwzu" & 0xfc000000 == 0x84000000; + "lwzux" & 0xfc0007ff == 0x7c00006e; + "lwzx" & 0xfc0007ff == 0x7c00002e; + "mcrf" & 0xfc300fff == 0x4c000000; + "mcrfs" & 0xfc30ffff == 0xfc000080; + "mcrxr" & 0xfc30ffff == 0x7c000400; + "mfcr" & 0xfc1fffff == 0x7c000026; + "mffs" & 0xfc1ffffe == 0x7c00048e; + "mfmsr" & 0xfc1fffff == 0x7c0000a6; + "mfspr" & 0xfc0007ff == 0x7c0002a6; + "mfsr" & 0xfc10ffff == 0x7c0004a6; + "mfsrin" & 0xfc1f07ff == 0x7c000526; + "mftb" & 0xfc0007ff == 0x7c0002e6; + "mtcrf" & 0xfc100fff == 0x7c000120; + "mtfsb0" & 0xfc1ffffe == 0xfc00008c; + "mtfsb1" & 0xfc1ffffe == 0xfc00004c; + "mtfsf" & 0xfe0107fe == 0xfc00058e; + "mtfsfi" & 0xfc7f0ffe == 0xfc00010c; + "mtmsr" & 0xfc1fffff == 0x7c000124; + "mtspr" & 0xfc0007ff == 0x7c0003a6; + "mtsr" & 0xfc10ffff == 0x7c0001a4; + "mtsrin" & 0xfc1f07ff == 0x7c0001e4; + "mulhw" & 0xfc0007fe == 0x7c000096; + "mulhwu" & 0xfc0007fe == 0x7c000016; + "mulli" & 0xfc000000 == 0x1c000000; + "mullw" & 0xfc0003fe == 0x7c0001d6; + "nand" & 0xfc0007fe == 0x7c0003b8; + "neg" & 0xfc00fffe == 0x7c0000d0; + "nor" & 0xfc0007fe == 0x7c0000f8; + "or" & 0xfc0007fe == 0x7c000378; + "orc" & 0xfc0007fe == 0x7c000338; + "ori" & 0xfc000000 == 0x60000000; + "oris" & 0xfc000000 == 0x64000000; + "psq_l" & 0xfc000000 == 0xe0000000; + "psq_lu" & 0xfc000000 == 0xe4000000; + "psq_lux" & 0xfc00007f == 0x1000004c; + "psq_lx" & 0xfc00007f == 0x1000000c; + "psq_st" & 0xfc000000 == 0xf0000000; + "psq_stu" & 0xfc000000 == 0xf4000000; + "psq_stux" & 0xfc00007f == 0x1000004e; + "psq_stx" & 0xfc00007f == 0x1000000e; + "ps_abs" & 0xfc1f07fe == 0x10000210; + "ps_add" & 0xfc0007fe == 0x1000002a; + "ps_cmpo0" & 0xfc6007ff == 0x10000040; + "ps_cmpo1" & 0xfc6007ff == 0x100000c0; + "ps_cmpu0" & 0xfc6007ff == 0x10000000; + "ps_cmpu1" & 0xfc6007ff == 0x10000080; + "ps_div" & 0xfc0007fe == 0x10000024; + "ps_madd" & 0xfc00003e == 0x1000003a; + "ps_madds0" & 0xfc00003e == 0x1000001c; + "ps_madds1" & 0xfc00003e == 0x1000001e; + "ps_merge00" & 0xfc0007fe == 0x10000420; + "ps_merge01" & 0xfc0007fe == 0x10000460; + "ps_merge10" & 0xfc0007fe == 0x100004a0; + "ps_merge11" & 0xfc0007fe == 0x100004e0; + "ps_mr" & 0xfc1f07fe == 0x10000090; + "ps_msub" & 0xfc00003e == 0x10000038; + "ps_mul" & 0xfc00f83e == 0x10000032; + "ps_muls0" & 0xfc00f83e == 0x10000018; + "ps_muls1" & 0xfc00f83e == 0x1000001a; + "ps_nabs" & 0xfc1f07fe == 0x10000110; + "ps_neg" & 0xfc1f07fe == 0x10000050; + "ps_nmadd" & 0xfc00003e == 0x1000003e; + "ps_nmsub" & 0xfc00003e == 0x1000003c; + "ps_res" & 0xfc1f07fe == 0x10000030; + "ps_rsqrte" & 0xfc1f07fe == 0x10000034; + "ps_sel" & 0xfc00003e == 0x1000002e; + "ps_sub" & 0xfc0007fe == 0x10000028; + "ps_sum0" & 0xfc00003e == 0x10000014; + "ps_sum1" & 0xfc00003e == 0x10000016; + "rfi" & 0xfffff801 == 0x4c000000; + "rlwimi" & 0xfc000000 == 0x50000000; + "rlwinm" & 0xfc000000 == 0x54000000; + "rlwnm" & 0xfc000000 == 0x5c000000; + "sc" & 0xffffffff == 0x44000002; + "slw" & 0xfc0007fe == 0x7c000030; + "sraw" & 0xfc0007fe == 0x7c000630; + "srawi" & 0xfc0007fe == 0x7c000670; + "srw" & 0xfc0007fe == 0x7c000430; + "stb" & 0xfc000000 == 0x98000000; + "stbu" & 0xfc000000 == 0x9c000000; + "stbux" & 0xfc0003ff == 0x7c0001ee; + "stbx" & 0xfc0003ff == 0x7c0001ae; + "stfd" & 0xfc000000 == 0xd8000000; + "stfdu" & 0xfc000000 == 0xdc000000; + "stfdux" & 0xfc0003ff == 0x7c0005ee; + "stfdx" & 0xfc0003ff == 0x7c0005ae; + "stfiwx" & 0xfc0007ff == 0x7c0007ae; + "stfs" & 0xfc000000 == 0xd0000000; + "stfsu" & 0xfc000000 == 0xd4000000; + "stfsux" & 0xfc0003ff == 0x7c00056e; + "stfsx" & 0xfc0003ff == 0x7c00052e; + "sth" & 0xfc000000 == 0xb0000000; + "sthbrx" & 0xfc0007ff == 0x7c00072c; + "sthu" & 0xfc000000 == 0xb4000000; + "sthux" & 0xfc0003ff == 0x7c00036e; + "sthx" & 0xfc0003ff == 0x7c00032e; + "stmw" & 0xfc000000 == 0xbc000000; + "stswi" & 0xfc0007ff == 0x7c0005aa; + "stswx" & 0xfc0007ff == 0x7c00052a; + "stw" & 0xfc000000 == 0x90000000; + "stwbrx" & 0xfc0007ff == 0x7c00052c; + "stwcx." & 0xfc0007ff == 0x7c00012d; + "stwu" & 0xfc000000 == 0x94000000; + "stwux" & 0xfc0003ff == 0x7c00016e; + "stwx" & 0xfc0003ff == 0x7c00012e; + "subf" & 0xfc0003fe == 0x7c000050; + "subfc" & 0xfc0003fe == 0x7c000010; + "subfe" & 0xfc0003fe == 0x7c000110; + "subfic" & 0xfc000000 == 0x20000000; + "subfme" & 0xfc00fbfe == 0x7c0001d0; + "subfze" & 0xfc00fbfe == 0x7c000190; + "sync" & 0xffffffff == 0x7c0004ac; + "tlbie" & 0xffff07ff == 0x7c000264; + "tlbsync" & 0xffffffff == 0x7c00046c; + "tw" & 0xfc0007ff == 0x7c000008; + "twi" & 0xfc000000 == 0xc000000; + "xor" & 0xfc0007fe == 0x7c000278; + "xori" & 0xfc000000 == 0x68000000; + "xoris" & 0xfc000000 == 0x6c000000; } impl Default for Opcode { diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 0000000..868fbfa --- /dev/null +++ b/macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ppc750cl-macros" +version = "0.1.0" +edition = "2018" +authors = ["Richard Patel "] + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.28" +syn = { version = "1.0.74", features = ["full"] } diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 0000000..ec4cb87 --- /dev/null +++ b/macros/src/lib.rs @@ -0,0 +1,144 @@ +use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; +use std::iter::FromIterator; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::{And, EqEq}; +use syn::{LitInt, LitStr, Token}; + +struct Opcodes { + opcodes: Punctuated, +} + +impl Parse for Opcodes { + fn parse(input: ParseStream) -> syn::Result { + Ok(Self { + opcodes: Punctuated::parse_terminated(input)?, + }) + } +} + +#[derive(Debug)] +struct Opcode { + name: String, + variant_name: String, + mask: u32, + bits: u32, +} + +impl Parse for Opcode { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse::()?; + input.parse::()?; + let mask = input.parse::()?; + input.parse::()?; + let bits = input.parse::()?; + Ok(Self { + name: name.value(), + variant_name: opcode_to_variant_name(&name)?, + mask: hex_from_lit_int(&mask)?, + bits: hex_from_lit_int(&bits)?, + }) + } +} + +fn hex_from_lit_int(int: &LitInt) -> syn::Result { + let str = int.to_string(); + let hex = match str.strip_prefix("0x") { + None => return Err(syn::Error::new(int.span(), "not a hex integer")), + Some(x) => x, + }; + u32::from_str_radix(hex, 16).map_err(|e| syn::Error::new(int.span(), e)) +} + +fn opcode_to_variant_name(opcode: &LitStr) -> syn::Result { + let mut s = String::new(); + let opcode_value = opcode.value(); + let mut chars = opcode_value.chars(); + loop { + // Make first char uppercase. + let c = match chars.next() { + None => return Ok(s), + Some(c) => c, + }; + match c { + 'a'..='z' => s.push(c.to_ascii_uppercase()), + _ => return Err(syn::Error::new(opcode.span(), "invalid opcode name")), + } + loop { + let c = match chars.next() { + None => return Ok(s), + Some(c) => c, + }; + match c { + '0'..='9' | 'a'..='z' => s.push(c), + '_' => break, + '.' => { + s.push('_'); + break; + } + _ => { + return Err(syn::Error::new( + opcode.span(), + "invalid character in opcode name", + )) + } + } + } + } +} + +#[proc_macro] +pub fn isa(input: TokenStream) -> TokenStream { + let opcodes = syn::parse_macro_input!(input as Opcodes); + + // Assemble root stream. + let mut root = Vec::::new(); + + // Define enum derives and header. + let derives: TokenStream = "#[derive(Debug, Copy, Clone, Eq, PartialEq)]" + .parse() + .unwrap(); + root.append(&mut derives.into_iter().collect()); + let enum_header: TokenStream = "pub enum Opcode".parse().unwrap(); + root.append(&mut enum_header.into_iter().collect()); + + // Create entries. + // First entry is going to be the illegal entry. + let mut enum_entries = Vec::::new(); + enum_entries.append( + &mut "Illegal = -1," + .parse::() + .unwrap() + .into_iter() + .collect(), + ); + // Append the actual opcodes. + for opcode in opcodes.opcodes { + enum_entries.push(TokenTree::Ident(Ident::new( + &opcode.variant_name, + Span::call_site(), + ))); + enum_entries.push(TokenTree::Punct(Punct::new(',', Spacing::Alone))); + } + + // Create body. + let enum_body = Group::new(Delimiter::Brace, TokenStream::from_iter(enum_entries)); + root.push(TokenTree::Group(enum_body)); + + TokenStream::from_iter(root) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn _opcode_to_variant_name(input: &str) -> String { + opcode_to_variant_name(&LitStr::new(input, proc_macro2::Span::call_site())).unwrap() + } + + #[test] + fn test_opcode_to_variant_name() { + assert_eq!(_opcode_to_variant_name("lol_lel."), "LolLel_"); + assert_eq!(_opcode_to_variant_name("ps_nmsub"), "PsNmsub"); + } +}