From f1f33d858699baad62c78e5e4262e55fbde41a52 Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Wed, 26 Jun 2019 12:05:53 +0300 Subject: [PATCH] Improve debugger repl parsing. Add assignment expressions, add tests, and cleanup code. --- src/debugger/mod.rs | 374 +++++++++++++++++++++++------------------ src/debugger/parser.rs | 167 ++++++++++++------ 2 files changed, 331 insertions(+), 210 deletions(-) diff --git a/src/debugger/mod.rs b/src/debugger/mod.rs index d31a27e..f600292 100644 --- a/src/debugger/mod.rs +++ b/src/debugger/mod.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use rustyline::error::ReadlineError; use rustyline::Editor; @@ -13,22 +11,15 @@ use super::sysbus::SysBus; use crate::disass::Disassembler; mod parser; -use parser::{parse_command_line, ParsedLine, Argument}; - -#[derive(Debug)] -pub struct Debugger { - cpu: cpu::Core, - sysbus: SysBus, - running: bool, - breakpoints: Vec, -} +use parser::{parse_expr, Expr, Value}; #[derive(Debug, PartialEq)] pub enum DebuggerError { ParsingError(String), CpuError(cpu::CpuError), InvalidCommand(String), - InvalidArgument(String) + InvalidArgument(String), + InvalidCommandFormat(String), } impl From for DebuggerError { @@ -40,81 +31,110 @@ impl From for DebuggerError { type DebuggerResult = Result; #[derive(Debug, PartialEq)] -pub enum DebuggerCommand { +pub enum Command { Info, SingleStep, Continue, HexDump(u32, usize), - Disass { addr: u32, num_opcodes: usize }, + Disass(u32, usize), AddBreakpoint(u32), ListBreakpoints, Reset, Quit, } -use DebuggerCommand::*; -impl TryFrom for DebuggerCommand { - type Error = DebuggerError; +impl Command { + fn run(&self, debugger: &mut Debugger) { + use Command::*; + match *self { + Info => println!("{}", debugger.cpu), + SingleStep => { + match debugger.cpu.step(&mut debugger.sysbus) { + Ok(_) => (), + Err(e) => { + println!("{}: {}", "cpu encountered an error".red(), e); + println!("cpu: {:#x?}", debugger.cpu); + } + }; + if debugger.is_breakpoint_reached() { + println!("breakpoint 0x{:08x} reached!", debugger.cpu.pc) + } + } + Continue => loop { + match debugger.cpu.step(&mut debugger.sysbus) { + Ok(_) => (), + Err(e) => { + println!("{}: {}", "cpu encountered an error".red(), e); + println!("cpu: {:#x?}", debugger.cpu); + break; + } + }; + if debugger.is_breakpoint_reached() { + println!("breakpoint 0x{:08x} reached!", debugger.cpu.pc) + } + }, + HexDump(addr, nbytes) => { + let bytes = match debugger.sysbus.get_bytes(addr, nbytes) { + Some(bytes) => bytes, + None => { + println!("requested content out of bounds"); + return; + } + }; + hexdump::hexdump(bytes); + } + Disass(addr, n) => { + let bytes = match debugger.sysbus.get_bytes(addr, 4 * n) { + Some(bytes) => bytes, + None => { + println!("requested content out of bounds"); + return; + } + }; + let disass = Disassembler::new(addr, bytes); + for line in disass { + println!("{}", line) + } - fn try_from(value: ParsedLine) -> Result { - match value.command.as_ref() { - "i" | "info" => { - Ok(Info) - }, - "s" | "step" => Ok(SingleStep), - "c" | "continue" => Ok(Continue), - "xxd" => { - let mut args = value.args.into_iter(); - let addr = match args.next() { - Some(Argument::Num(n)) => n, - _ => { - return Err(DebuggerError::InvalidArgument(format!("expected a number"))); - } - }; - let nbytes = match args.next() { - Some(Argument::Num(n)) => n, - _ => { - return Err(DebuggerError::InvalidArgument(format!("expected a number"))); - } - }; - let nbytes = nbytes as usize; - Ok(HexDump(addr, nbytes)) + debugger.next_disass_addr = Some(addr + (4 * n as u32)); } - "d" | "disass" => { - let mut args = value.args.into_iter(); - let addr = match args.next() { - Some(Argument::Num(n)) => n, - _ => { - return Err(DebuggerError::InvalidArgument(format!("expected a number"))); - } - }; - let num_opcodes = match args.next() { - Some(Argument::Num(n)) => n, - _ => { - return Err(DebuggerError::InvalidArgument(format!("expected a number"))); - } - }; - let num_opcodes = num_opcodes as usize; - Ok(Disass { addr, num_opcodes }) + Quit => { + print!("Quitting!"); + debugger.stop(); } - "b" | "break" => { - let mut args = value.args.into_iter(); - let addr = match args.next() { - Some(Argument::Num(n)) => n, - _ => { - return Err(DebuggerError::InvalidArgument(format!("expected a number"))); - } - }; - Ok(AddBreakpoint(addr)) + AddBreakpoint(addr) => { + if !debugger.breakpoints.contains(&addr) { + let new_index = debugger.breakpoints.len(); + debugger.breakpoints.push(addr); + println!("added breakpoint [{}] 0x{:08x}", new_index, addr); + } else { + println!("breakpoint already exists!") + } + } + ListBreakpoints => { + println!("breakpoint list:"); + for (i, b) in debugger.breakpoints.iter().enumerate() { + println!("[{}] 0x{:08x}", i, b) + } + } + Reset => { + println!("resetting cpu..."); + debugger.cpu.reset(); + println!("cpu is restarted!") } - "bl" => Ok(ListBreakpoints), - "q" | "quit" => Ok(Quit), - "r" | "reset" => Ok(Reset), - _ => Err(DebuggerError::InvalidCommand(value.command)), } } } +#[derive(Debug)] +pub struct Debugger { + cpu: cpu::Core, + sysbus: SysBus, + running: bool, + breakpoints: Vec, + next_disass_addr: Option, +} + impl Debugger { pub fn new(cpu: cpu::Core, sysbus: SysBus) -> Debugger { Debugger { @@ -122,6 +142,7 @@ impl Debugger { sysbus: sysbus, breakpoints: Vec::new(), running: false, + next_disass_addr: None, } } @@ -136,85 +157,127 @@ impl Debugger { false } - fn command(&mut self, cmd: DebuggerCommand) { - match cmd { - Info => println!("cpu info: {:#x?}", self.cpu), - SingleStep => { - match self.cpu.step(&mut self.sysbus) { - Ok(_) => (), - Err(e) => { - println!("{}: {}", "cpu encountered an error".red(), e); - println!("cpu: {:#x?}", self.cpu); - } - }; - if self.is_breakpoint_reached() { - println!("breakpoint 0x{:08x} reached!", self.cpu.pc) - } - } - Continue => loop { - match self.cpu.step(&mut self.sysbus) { - Ok(_) => (), - Err(e) => { - println!("{}: {}", "cpu encountered an error".red(), e); - println!("cpu: {:#x?}", self.cpu); - break; - } - }; - if self.is_breakpoint_reached() { - println!("breakpoint 0x{:08x} reached!", self.cpu.pc) - } - }, - HexDump(addr, nbytes) => { - let bytes = match self.sysbus.get_bytes(addr, nbytes) { - Some(bytes) => bytes, - None => { - println!("requested content out of bounds"); - return; - } - }; - hexdump::hexdump(bytes); - } - Disass { addr, num_opcodes } => { - let bytes = match self.sysbus.get_bytes(addr, 4 * num_opcodes) { - Some(bytes) => bytes, - None => { - println!("requested content out of bounds"); - return; - } - }; - let disass = Disassembler::new(addr, bytes); - for line in disass { - println!("{}", line) - } - } - Quit => { - print!("Quitting!"); - self.running = false; - } - AddBreakpoint(addr) => { - if !self.breakpoints.contains(&addr) { - let new_index = self.breakpoints.len(); - self.breakpoints.push(addr); - println!("added breakpoint [{}] 0x{:08x}", new_index, addr); - } else { - println!("breakpoint already exists!") - } - } - ListBreakpoints => { - println!("breakpoint list:"); - for (i, b) in self.breakpoints.iter().enumerate() { - println!("[{}] 0x{:08x}", i, b) - } - } - Reset => { - println!("resetting cpu..."); - self.cpu.reset(); - println!("cpu is restarted!") - } - _ => panic!("command {:?} not implemented", cmd), + fn decode_register(&self, s: &str) -> DebuggerResult { + let reg_names = vec![ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", "lr", + "pc"]; + + match reg_names.into_iter().position(|r| r == s) { + Some(index) => Ok(index), + None => Err(DebuggerError::InvalidArgument(format!("{:?} is not a register name", s))) } } + + fn arg_reg(&self, arg: &Value) -> DebuggerResult { + match arg { + Value::Name(reg) => { + let reg = self.decode_register(®)?; + Ok(self.cpu.get_reg(reg)) + }, + v => Err(DebuggerError::InvalidArgument(format!("expected a number, got {:?}", v))) + } + } + + fn arg_number(&self, arg: &Value) -> DebuggerResult { + match arg { + Value::Num(n) => Ok(*n), + v => Err(DebuggerError::InvalidArgument(format!("expected a number, got {:?}", v))) + } + } + + fn arg_address(&self, arg: &Value) -> DebuggerResult { + match arg { + Value::Num(n) => Ok(*n), + Value::Name(reg) => { + let reg = self.decode_register(®)?; + Ok(self.cpu.get_reg(reg)) + } + v => Err(DebuggerError::InvalidArgument(format!("addr: expected a number or register, got {:?}", v))) + } + } + + fn eval_command(&self, command: Value, args: Vec) -> DebuggerResult { + let command = match command { + Value::Name(command) => command, + _ => { + return Err(DebuggerError::InvalidCommand("expected a name".to_string())); + } + }; + + match command.as_ref() { + "i" | "info" => Ok(Command::Info), + "s" | "step" => Ok(Command::SingleStep), + "c" | "continue" => Ok(Command::Continue), + "xxd" => { + let addr = self.arg_address(&args[0])?; + let nbytes = self.arg_number(&args[1])?; + Ok(Command::HexDump(addr, nbytes as usize)) + } + "d" | "disass" => { + let (addr, n) = match args.len() { + 2 => { + let addr = self.arg_address(&args[0])?; + let n = self.arg_number(&args[1])?; + + (addr, n as usize) + } + 1 => { + let addr = self.arg_address(&args[0])?; + + (addr, 10) + } + 0 => (self.next_disass_addr.unwrap_or(self.cpu.get_reg(15)), 10), + _ => { + return Err(DebuggerError::InvalidCommandFormat( + "disass [addr] [n]".to_string(), + )) + } + }; + + Ok(Command::Disass(addr, n)) + } + "b" | "break" => { + if args.len() != 1 { + Err(DebuggerError::InvalidCommandFormat( + "disass ".to_string(), + )) + } else { + let addr = self.arg_address(&args[0])?; + Ok(Command::AddBreakpoint(addr)) + } + } + "bl" => Ok(Command::ListBreakpoints), + "q" | "quit" => Ok(Command::Quit), + "r" | "reset" => Ok(Command::Reset), + _ => Err(DebuggerError::InvalidCommand(command)), + } + } + + fn eval_expr(&mut self, expr: Expr) { + match expr { + Expr::Command(c, a) => match self.eval_command(c, a) { + Ok(cmd) => cmd.run(self), + Err(DebuggerError::InvalidCommand(c)) => { + println!("{}: {:?}", "invalid command".red(), c) + } + Err(DebuggerError::InvalidArgument(m)) => { + println!("{}: {}", "invalid argument".red(), m) + } + Err(DebuggerError::InvalidCommandFormat(m)) => { + println!("help: {}", m.bright_yellow()) + } + Err(e) => println!("{} {:?}", "failed to build command".red(), e), + }, + Expr::Assignment(_, _) => unimplemented!("assignments are not implemented"), + Expr::Empty => println!("Got empty expr"), + } + } + + fn stop(&mut self) { + self.running = false; + } + pub fn repl(&mut self) -> DebuggerResult<()> { self.running = true; let mut rl = Editor::<()>::new(); @@ -223,22 +286,13 @@ impl Debugger { match readline { Ok(line) => { if line.is_empty() { - continue + continue; } - let line = parse_command_line(&line); - match line { - Ok(line) => { - match DebuggerCommand::try_from(line) { - Ok(cmd) => self.command(cmd), - Err(DebuggerError::InvalidCommand(c)) => println!("{}: {:?}", "invalid command".red(), c), - Err(DebuggerError::InvalidArgument(m)) => println!("{}: {}", "invalid argument".red(), m), - _ => () - } - } - Err(DebuggerError::ParsingError(msg)) => { - println!("Parsing error: {}", msg) - } - _ => () + let expr = parse_expr(&line); + match expr { + Ok(expr) => self.eval_expr(expr), + Err(DebuggerError::ParsingError(msg)) => println!("Parsing error: {}", msg), + _ => (), } } Err(ReadlineError::Interrupted) => { diff --git a/src/debugger/parser.rs b/src/debugger/parser.rs index 794d3fa..6fc3854 100644 --- a/src/debugger/parser.rs +++ b/src/debugger/parser.rs @@ -3,34 +3,29 @@ use std::fmt; use nom; use nom::branch::alt; use nom::bytes::complete::{tag, take_while_m_n}; -use nom::character::complete::{alphanumeric0, digit1, multispace0, multispace1}; -use nom::combinator::{cut, map, map_res}; +use nom::character::complete::{alphanumeric1, char, digit1, multispace0, multispace1}; +use nom::combinator::{cut, map, map_res, opt}; use nom::error::{context, convert_error, ParseError, VerboseError}; -use nom::multi::separated_list; -use nom::sequence::{preceded, terminated}; +use nom::multi::{many0, separated_list}; +use nom::sequence::{delimited, preceded, separated_pair, terminated, tuple}; use nom::IResult; use super::{DebuggerError, DebuggerResult}; #[derive(Debug, PartialEq, Clone)] -pub enum Argument { +pub enum Value { Num(u32), Boolean(bool), + Name(String), } -impl fmt::Display for Argument { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Argument::Num(_) => write!(f, "number"), - Argument::Boolean(_) => write!(f, "boolean"), - } - } -} - -#[derive(Debug)] -pub struct ParsedLine { - pub command: String, - pub args: Vec, +#[derive(Debug, PartialEq)] +pub enum Expr { + /// (command-name arg0 arg1 ...) + Command(Value, Vec), + /// constant = value + Assignment(Value, Value), + Empty, } fn parse_u32_hex<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, u32, E> { @@ -44,58 +39,130 @@ fn parse_u32<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, u32, E> context("u32", map_res(digit1, |s| u32::from_str_radix(s, 10)))(i) } -fn parse_num<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Argument, E> { - map(alt((parse_u32_hex, parse_u32)), |n| Argument::Num(n))(i) +fn parse_num<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> { + map(alt((parse_u32_hex, parse_u32)), |n| Value::Num(n))(i) } -fn parse_boolean<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Argument, E> { +fn parse_boolean<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> { context( "bool", alt(( - map(tag("true"), |_| Argument::Boolean(true)), - map(tag("false"), |_| Argument::Boolean(false)), + map(tag("true"), |_| Value::Boolean(true)), + map(tag("false"), |_| Value::Boolean(false)), )), )(i) } -fn parse_argument<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Argument, E> { - context("argument", alt((parse_num, parse_boolean)))(i) +fn parse_name<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> { + map(alphanumeric1, |s: &str| Value::Name(String::from(s)))(i) } -fn parse_argument_list<'a, E: ParseError<&'a str>>( - i: &'a str, -) -> IResult<&'a str, Vec, E> { +fn parse_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> { + context("argument", alt((parse_boolean, parse_num, parse_name)))(i) +} + +fn parse_command<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Expr, E> { context( - "argument list", - preceded(multispace0, terminated(separated_list(multispace1, parse_argument), multispace0)) + "command", + map( + tuple(( + terminated(parse_name, multispace0), + separated_list(multispace1, parse_value), + )), + |(cmd, args)| Expr::Command(cmd, args), + ), )(i) } -fn parse_command_name<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> { - context("command", preceded(multispace0, cut(alphanumeric0)))(i) +fn parse_assignment<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Expr, E> { + context( + "assignment", + map( + separated_pair( + parse_value, + preceded(multispace0, char('=')), + preceded(multispace0, parse_value), + ), + |(lvalue, rvalue)| Expr::Assignment(lvalue, rvalue), + ), + )(i) } -fn parse_line<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, ParsedLine, E> { - let (i, command_name) = parse_command_name(i)?; - println!("command name = {:?}, remaining {:?}", command_name, i); - let (i, arguments) = parse_argument_list(i)?; - println!("command: {} arguments: {:?}", command_name, arguments); - - Ok(( - i, - ParsedLine { - command: command_name.to_string(), - args: arguments, - }, - )) +fn _parse_expr<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Expr, E> { + context( + "expression", + preceded( + multispace0, + alt(( + parse_assignment, + parse_command, + map(multispace0, |_| Expr::Empty), + )), + ), + )(i) } -pub fn parse_command_line(input: &str) -> DebuggerResult { - match parse_line::>(input) { - Ok((_, line)) => Ok(line), +pub fn parse_expr(i: &str) -> DebuggerResult { + match _parse_expr::>(i) { + Ok((_, expr)) => Ok(expr), Err(nom::Err::Failure(e)) | Err(nom::Err::Error(e)) => { - Err(DebuggerError::ParsingError(convert_error(input, e))) + Err(DebuggerError::ParsingError(convert_error(i, e))) } _ => panic!("unhandled parser error"), } -} \ No newline at end of file +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_empty_expr() { + assert_eq!(parse_expr(" "), Ok(Expr::Empty)); + } + + #[test] + fn test_parse_command_expr() { + assert_eq!( + parse_expr("command"), + Ok(Expr::Command(Value::Name("command".to_string()), vec![])) + ); + assert_eq!( + parse_expr("command arg0 0x1337 true "), + Ok(Expr::Command( + Value::Name("command".to_string()), + vec![ + Value::Name("arg0".to_string()), + Value::Num(0x1337), + Value::Boolean(true) + ] + )) + ); + } + + #[test] + fn test_parse_assignment_expr() { + assert_eq!( + parse_expr(" pc = 0x1337 "), + Ok(Expr::Assignment( + Value::Name("pc".to_string()), + Value::Num(0x1337) + )) + ); + assert_eq!( + parse_expr("aaa = false "), + Ok(Expr::Assignment( + Value::Name("aaa".to_string()), + Value::Boolean(false) + )) + ); + assert_eq!( + parse_expr(" pc = lr "), + Ok(Expr::Assignment( + Value::Name("pc".to_string()), + Value::Name("lr".to_string()) + )) + ); + } + +}