1use crate::instruction_table::InstructionTable;
23use crate::table::Table;
24use crate::write_once_table::WriteOnceTable;
25use std::fmt;
26
27pub struct Builder<'a, T: 'a + fmt::Debug + PartialEq> {
35 pub instruction_table: &'a InstructionTable<T>,
36 pub instructions: Vec<usize>,
37 pub labels: WriteOnceTable<usize>,
38 pub data: Vec<T>,
39}
40
41impl<'a, T: fmt::Debug + PartialEq> Builder<'a, T> {
42 pub fn new(instruction_table: &'a InstructionTable<T>) -> Builder<T> {
44 let mut labels = WriteOnceTable::new();
45 labels.insert("main", 0);
46 Builder {
47 instruction_table: &instruction_table,
48 instructions: vec![],
49 labels,
50 data: vec![],
51 }
52 }
53
54 pub fn push(&mut self, name: &str, args: Vec<T>) {
60 let instr = self
61 .instruction_table
62 .by_name(name)
63 .unwrap_or_else(|| panic!("Unable to find instruction with name {:?}", name));
64
65 if args.len() != instr.arity {
66 panic!(
67 "Instruction {} has arity of {}, but you provided {} arguments.",
68 instr.name,
69 instr.arity,
70 args.len()
71 )
72 }
73
74 self.instructions.push(instr.op_code);
75 self.instructions.push(instr.arity);
76 for arg in args {
77 let pos = self.push_data(arg);
78 self.instructions.push(pos);
79 }
80 }
81
82 pub fn label(&mut self, name: &str) {
87 let idx = self.instructions.len();
88 self.labels.insert(name, idx);
89 }
90
91 pub fn len(&self) -> usize {
95 self.instructions.len()
96 }
97
98 pub fn is_empty(&self) -> bool {
100 self.instructions.is_empty()
101 }
102
103 fn push_data(&mut self, data: T) -> usize {
104 let pos = self.data.iter().position(|d| d == &data);
105 match pos {
106 Some(pos) => pos,
107 None => {
108 self.data.push(data);
109 self.data.len() - 1
110 }
111 }
112 }
113}
114
115impl<'a, T: 'a + fmt::Debug + PartialEq> fmt::Debug for Builder<'a, T> {
116 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117 let mut result = String::new();
118
119 for i in 0..self.data.len() {
120 result.push_str(&format!("@{} = {:?}\n", i, self.data[i]));
121 }
122
123 let mut ip = 0;
124 let len = self.instructions.len();
125 loop {
126 for label in self.labels.keys() {
127 let idx = *self.labels.get(&label).unwrap();
128 if idx == ip {
129 result.push_str(&format!("\n.{}:\n", label));
130 }
131 }
132
133 if ip == len {
134 break;
135 }
136
137 let op_code = self.instructions[ip];
138 ip += 1;
139 let arity = self.instructions[ip];
140
141 let instr = self
142 .instruction_table
143 .by_op_code(op_code)
144 .unwrap_or_else(|| panic!("Unable to find instruction with op code {}", op_code));
145
146 result.push_str(&format!("\t{}", &instr.name));
147
148 for _j in 0..arity {
149 ip += 1;
150 let const_idx = self.instructions[ip];
151 result.push_str(&format!(" @{}", const_idx));
152 }
153 result.push_str("\n");
154
155 ip += 1;
156 }
157
158 write!(f, "{}", result)
159 }
160}
161
162#[cfg(test)]
163mod test {
164 use super::*;
165 use crate::instruction::Instruction;
166 use crate::instruction_table::InstructionTable;
167 use crate::machine::Machine;
168
169 fn noop(_machine: &mut Machine<usize>, _args: &[usize]) {}
170
171 fn example_instruction_table() -> InstructionTable<usize> {
172 let mut it = InstructionTable::new();
173 it.insert(Instruction::new(0, "noop", 0, noop));
174 it.insert(Instruction::new(1, "push", 1, noop));
175 it.insert(Instruction::new(2, "pop", 0, noop));
176 it
177 }
178
179 #[test]
180 fn new() {
181 let it = example_instruction_table();
182 let builder: Builder<usize> = Builder::new(&it);
183 assert!(builder.instructions.is_empty());
184 }
185
186 #[test]
187 fn push() {
188 let it = example_instruction_table();
189 let mut builder: Builder<usize> = Builder::new(&it);
190 builder.push("noop", vec![]);
191 assert!(!builder.instructions.is_empty());
192 }
193
194 #[test]
195 #[should_panic(expected = "has arity of")]
196 fn push_with_incorrect_arity() {
197 let it = example_instruction_table();
198 let mut builder: Builder<usize> = Builder::new(&it);
199 builder.push("noop", vec![1]);
200 }
201
202 #[test]
203 fn label() {
204 let it = example_instruction_table();
205 let mut builder: Builder<usize> = Builder::new(&it);
206 builder.push("noop", vec![]);
207 builder.label("wow");
208 assert_eq!(*builder.labels.get("wow").unwrap(), 2);
209 }
210
211 #[test]
212 fn data_is_deduped() {
213 let it = example_instruction_table();
214 let mut builder: Builder<usize> = Builder::new(&it);
215 builder.push("push", vec![123]);
216 builder.push("push", vec![123]);
217 builder.push("push", vec![123]);
218 assert_eq!(builder.data.len(), 1);
219 }
220
221 #[test]
222 fn debug_format() {
223 let it = example_instruction_table();
224 let mut builder: Builder<usize> = Builder::new(&it);
225 builder.push("noop", vec![]);
226 builder.push("push", vec![123]);
227 builder.push("push", vec![456]);
228 builder.label("some_function");
229 builder.push("pop", vec![]);
230
231 let actual = format!("{:?}", builder);
232 let expected = "@0 = 123
233@1 = 456
234
235.main:
236\tnoop
237\tpush @0
238\tpush @1
239
240.some_function:
241\tpop
242";
243 assert_eq!(actual, expected);
244 }
245}