essential_asm_spec/
lib.rs1use serde::Deserialize;
5
6mod de;
7pub mod visit;
8
9pub const ASM_YAML: &str = include_str!("./../asm.yml");
11
12#[derive(Debug)]
15pub struct Tree(Vec<(String, Node)>);
16
17#[derive(Debug)]
19pub enum Node {
20 Op(Op),
21 Group(Group),
22}
23
24#[derive(Debug, Deserialize)]
26pub struct Group {
27 pub description: String,
28 #[serde(rename = "group")]
29 pub tree: Tree,
30}
31
32#[derive(Debug, Deserialize)]
36pub struct Op {
37 pub opcode: u8,
38 pub description: String,
39 #[serde(default)]
40 pub short: String,
41 #[serde(default)]
42 pub panics: Vec<String>,
43 #[serde(default)]
44 pub num_arg_bytes: u8,
45 #[serde(default)]
46 pub stack_in: Vec<String>,
47 #[serde(default)]
48 pub stack_out: StackOut,
49}
50
51#[derive(Debug)]
53pub enum StackOut {
54 Fixed(Vec<String>),
55 Dynamic(StackOutDynamic),
56}
57
58#[derive(Debug, Deserialize)]
60pub struct StackOutDynamic {
61 pub elem: String,
62 pub len: String,
63}
64
65impl Node {
66 fn opcode(&self) -> u8 {
70 match self {
71 Self::Op(op) => op.opcode,
72 Self::Group(group) => group.tree.first().unwrap().1.opcode(),
73 }
74 }
75}
76
77impl Default for StackOut {
78 fn default() -> Self {
79 Self::Fixed(vec![])
80 }
81}
82
83impl std::ops::Deref for Tree {
84 type Target = Vec<(String, Node)>;
85 fn deref(&self) -> &Self::Target {
86 &self.0
87 }
88}
89
90pub fn tree() -> Tree {
92 serde_yaml::from_str::<Tree>(ASM_YAML)
93 .expect("ASM_YAML is a const and should never fail to deserialize")
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_deserialize() {
102 let tree = tree();
104 println!("{:#?}", tree);
105 }
106
107 #[test]
108 fn test_no_duplicate_opcodes() {
109 let tree = tree();
110 let mut opcodes = std::collections::BTreeSet::new();
111 super::visit::ops(&tree, &mut |name, op| {
112 assert!(
113 opcodes.insert(op.opcode),
114 "ASM YAML must not contain duplicate opcodes. \
115 Opcode `0x{:02X}` for {} already exists.",
116 op.opcode,
117 name.join(" "),
118 );
119 });
120 }
121
122 #[test]
123 fn test_visit_ordered_by_opcode() {
124 let tree = tree();
125 let mut last_opcode = 0;
126 super::visit::ops(&tree, &mut |_name, op| {
127 assert!(
128 last_opcode < op.opcode,
129 "Visit functions are expected to visit ops in opcode order.\n \
130 last opcode: `0x{last_opcode:02X}`\n \
131 this opcode: `0x{:02X}`",
132 op.opcode
133 );
134 last_opcode = op.opcode;
135 });
136 }
137}