1use ::core::convert::TryInto;
5use ::std::collections::BTreeSet;
6
7#[derive(Debug, Copy, Clone)]
8#[allow(dead_code)]
9#[repr(u16)]
10pub(super) enum NopTag {
11 LoopStart = 0,
12 LoopArgument,
13 LoopEnd,
14 FunctionStart,
15 FunctionArgument,
16 Breakpoint,
17 Unknown,
18}
19impl NopTag {
20 #[allow(dead_code)]
21 pub(super) fn code(&self) -> u16 {
22 *self as u16
23 }
24 pub(super) fn from_code(code: u16) -> Self {
25 if code >= NopTag::Unknown as u16 {
26 NopTag::Unknown
27 } else {
28 unsafe { ::core::mem::transmute(code) }
30 }
31 }
32}
33
34fn tagged<'a>(tag: &str, input: &'a str) -> Result<&'a str, ()> {
35 if input.starts_with(tag) {
36 Ok(&input[tag.len()..])
37 } else {
38 Err(())
39 }
40}
41
42fn parse_base_10_int(input: &str) -> Result<(&str, u64), ()> {
43 let mut cursor = input.as_bytes();
44 while let [b'0'..=b'9', rest @ ..] = cursor {
45 cursor = rest;
46 }
47 let len = cursor.as_ptr() as usize - input.as_ptr() as usize;
48 Ok((
49 &input[len..],
50 u64::from_str_radix(&input[..len], 10).unwrap(),
51 ))
52}
53
54pub(super) fn debugger_repl<R: ::rand::Rng>(machine: &mut super::Machine, rng: &mut R) {
55 let stdin = ::std::io::stdin();
56 let mut buf = String::with_capacity(1024);
57 let mut breakpoints = BTreeSet::<usize>::new();
58 while let Ok(_width) = stdin.read_line(&mut buf) {
59 if let Ok(rest) = tagged("print", &buf) {
60 if let Ok(rest) = tagged(" ", rest) {
61 if let Ok(_rest) = tagged("ip", rest) {
62 println!("IP = {}", machine.instruction_pointer);
63 } else if let Ok(_rest) = tagged("temp", rest) {
64 println!("Temporaries: {:?}", machine.value_stack);
65 } else if let Ok(_rest) = tagged("vars", rest) {
66 println!("Vars: {:?}", machine.var_stack);
67 } else if let Ok(rest) = tagged("out", rest) {
68 if let Ok(_rest) = tagged(" vars", rest) {
69 println!("Output Vars: {:?}", machine.output_vars);
70 } else {
71 println!("Output: {:?}", machine.output);
72 }
73 } else if let Ok(_rest) = tagged("fuel", rest) {
74 println!("Fuel: {}", machine.fuel);
75 }
76 } else {
77 dbg!(&*machine);
78 }
79 } else if let Ok(rest) = tagged("fmt", &buf) {
80 if let [.., top] = &*machine.output {
81 let buf = if let Ok(_rest) = tagged(" short", rest) {
82 machine
83 .stack_top()
84 .unwrap()
85 .to_int()
86 .map(|x| crate::mir::fmt::mbot_format_short(top, x))
87 } else {
88 machine
89 .stack_top()
90 .unwrap()
91 .to_int()
92 .map(|x| crate::mir::fmt::mbot_format_default(top, x))
93 };
94 match buf {
95 Ok(buf) => println!("{}", buf),
96 Err(e) => println!("Failed to format: {:?}", e),
97 }
98 } else {
99 println!("No structured output to format.");
100 }
101 } else if let Ok(_rest) = tagged("bt", &buf) {
102 println!("Backtrace: {:?}", machine.call_stack);
103 } else if let Ok(_rest) = tagged("step", &buf) {
104 let _ = dbg!(machine.step(rng));
106 } else if let Ok(_rest) = tagged("cont", &buf) {
107 use super::S;
108 loop {
109 let pre_ptr = machine.instruction_pointer;
110 match machine.step(rng) {
111 Ok(S::Normal) => {
112 if breakpoints.contains(&machine.instruction_pointer) {
113 println!(
114 "Reached (session) breakpoint at {}",
115 machine.instruction_pointer
116 );
117 break;
118 }
119 }
120 Ok(S::Break) => {
121 println!("Reached (compiled) breakpoint at {}", pre_ptr);
122 break;
123 }
124 Ok(S::Finished) => {
125 println!("Reached end of program.");
126 break;
127 }
128 Err(e) => {
129 println!("Program aborted: {:?}", e);
130 break;
131 }
132 }
133 }
134 } else if let Ok(_rest) = tagged("restart", &buf) {
135 machine.restart();
136 } else if let Ok(rest) = tagged("break", &buf) {
137 if let Ok(rest) = tagged(" ", rest) {
138 match parse_base_10_int(rest) {
139 Ok((_rest, pos)) => {
140 if breakpoints.insert(pos.try_into().unwrap()) {
141 println!("Set breakpoint at position {}.", pos)
142 } else {
143 println!("Position {} was already a breakpoint.", pos)
144 }
145 }
146 Err(()) => continue,
147 }
148 }
149 } else if let Ok(rest) = tagged("fuel", &buf) {
150 if let Ok(rest) = tagged(" ", rest) {
151 match parse_base_10_int(rest) {
152 Ok((_rest, amt)) => {
153 machine.add_fuel(amt);
154 }
155 Err(()) => continue,
156 }
157 } else {
158 machine.fuel += 1;
159 }
160 } else if let Ok(_rest) = tagged("compile core", &buf) {
161 #[cfg(feature = "script")]
162 {
163 let core = super::super::misp::Core::compile();
164 dbg!(&core);
165 }
166 #[cfg(not(feature = "script"))]
167 {
168 println!("this binary was not built with Misp support");
169 }
170 } else if let Ok(_rest) = tagged("exit", &buf) {
171 return;
172 }
173
174 buf.clear();
175 }
176}