Improve debugger repl parsing.
Add assignment expressions, add tests, and cleanup code.
This commit is contained in:
parent
a70b92d5a4
commit
f1f33d8586
|
@ -1,5 +1,3 @@
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::Editor;
|
use rustyline::Editor;
|
||||||
|
|
||||||
|
@ -13,22 +11,15 @@ use super::sysbus::SysBus;
|
||||||
use crate::disass::Disassembler;
|
use crate::disass::Disassembler;
|
||||||
|
|
||||||
mod parser;
|
mod parser;
|
||||||
use parser::{parse_command_line, ParsedLine, Argument};
|
use parser::{parse_expr, Expr, Value};
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Debugger {
|
|
||||||
cpu: cpu::Core,
|
|
||||||
sysbus: SysBus,
|
|
||||||
running: bool,
|
|
||||||
breakpoints: Vec<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DebuggerError {
|
pub enum DebuggerError {
|
||||||
ParsingError(String),
|
ParsingError(String),
|
||||||
CpuError(cpu::CpuError),
|
CpuError(cpu::CpuError),
|
||||||
InvalidCommand(String),
|
InvalidCommand(String),
|
||||||
InvalidArgument(String)
|
InvalidArgument(String),
|
||||||
|
InvalidCommandFormat(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<cpu::CpuError> for DebuggerError {
|
impl From<cpu::CpuError> for DebuggerError {
|
||||||
|
@ -40,81 +31,110 @@ impl From<cpu::CpuError> for DebuggerError {
|
||||||
type DebuggerResult<T> = Result<T, DebuggerError>;
|
type DebuggerResult<T> = Result<T, DebuggerError>;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DebuggerCommand {
|
pub enum Command {
|
||||||
Info,
|
Info,
|
||||||
SingleStep,
|
SingleStep,
|
||||||
Continue,
|
Continue,
|
||||||
HexDump(u32, usize),
|
HexDump(u32, usize),
|
||||||
Disass { addr: u32, num_opcodes: usize },
|
Disass(u32, usize),
|
||||||
AddBreakpoint(u32),
|
AddBreakpoint(u32),
|
||||||
ListBreakpoints,
|
ListBreakpoints,
|
||||||
Reset,
|
Reset,
|
||||||
Quit,
|
Quit,
|
||||||
}
|
}
|
||||||
use DebuggerCommand::*;
|
|
||||||
|
|
||||||
impl TryFrom<ParsedLine> for DebuggerCommand {
|
impl Command {
|
||||||
type Error = DebuggerError;
|
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<Self, Self::Error> {
|
debugger.next_disass_addr = Some(addr + (4 * n as u32));
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
"d" | "disass" => {
|
Quit => {
|
||||||
let mut args = value.args.into_iter();
|
print!("Quitting!");
|
||||||
let addr = match args.next() {
|
debugger.stop();
|
||||||
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 })
|
|
||||||
}
|
}
|
||||||
"b" | "break" => {
|
AddBreakpoint(addr) => {
|
||||||
let mut args = value.args.into_iter();
|
if !debugger.breakpoints.contains(&addr) {
|
||||||
let addr = match args.next() {
|
let new_index = debugger.breakpoints.len();
|
||||||
Some(Argument::Num(n)) => n,
|
debugger.breakpoints.push(addr);
|
||||||
_ => {
|
println!("added breakpoint [{}] 0x{:08x}", new_index, addr);
|
||||||
return Err(DebuggerError::InvalidArgument(format!("expected a number")));
|
} else {
|
||||||
}
|
println!("breakpoint already exists!")
|
||||||
};
|
}
|
||||||
Ok(AddBreakpoint(addr))
|
}
|
||||||
|
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<u32>,
|
||||||
|
next_disass_addr: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Debugger {
|
impl Debugger {
|
||||||
pub fn new(cpu: cpu::Core, sysbus: SysBus) -> Debugger {
|
pub fn new(cpu: cpu::Core, sysbus: SysBus) -> Debugger {
|
||||||
Debugger {
|
Debugger {
|
||||||
|
@ -122,6 +142,7 @@ impl Debugger {
|
||||||
sysbus: sysbus,
|
sysbus: sysbus,
|
||||||
breakpoints: Vec::new(),
|
breakpoints: Vec::new(),
|
||||||
running: false,
|
running: false,
|
||||||
|
next_disass_addr: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,85 +157,127 @@ impl Debugger {
|
||||||
false
|
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 {
|
fn decode_register(&self, s: &str) -> DebuggerResult<usize> {
|
||||||
println!("{}", line)
|
let reg_names = vec![
|
||||||
}
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", "lr",
|
||||||
}
|
"pc"];
|
||||||
Quit => {
|
|
||||||
print!("Quitting!");
|
match reg_names.into_iter().position(|r| r == s) {
|
||||||
self.running = false;
|
Some(index) => Ok(index),
|
||||||
}
|
None => Err(DebuggerError::InvalidArgument(format!("{:?} is not a register name", s)))
|
||||||
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 arg_reg(&self, arg: &Value) -> DebuggerResult<u32> {
|
||||||
|
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<u32> {
|
||||||
|
match arg {
|
||||||
|
Value::Num(n) => Ok(*n),
|
||||||
|
v => Err(DebuggerError::InvalidArgument(format!("expected a number, got {:?}", v)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg_address(&self, arg: &Value) -> DebuggerResult<u32> {
|
||||||
|
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<Value>) -> DebuggerResult<Command> {
|
||||||
|
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 <addr>".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<()> {
|
pub fn repl(&mut self) -> DebuggerResult<()> {
|
||||||
self.running = true;
|
self.running = true;
|
||||||
let mut rl = Editor::<()>::new();
|
let mut rl = Editor::<()>::new();
|
||||||
|
@ -223,22 +286,13 @@ impl Debugger {
|
||||||
match readline {
|
match readline {
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
let line = parse_command_line(&line);
|
let expr = parse_expr(&line);
|
||||||
match line {
|
match expr {
|
||||||
Ok(line) => {
|
Ok(expr) => self.eval_expr(expr),
|
||||||
match DebuggerCommand::try_from(line) {
|
Err(DebuggerError::ParsingError(msg)) => println!("Parsing error: {}", msg),
|
||||||
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)
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Interrupted) => {
|
Err(ReadlineError::Interrupted) => {
|
||||||
|
|
|
@ -3,34 +3,29 @@ use std::fmt;
|
||||||
use nom;
|
use nom;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::{tag, take_while_m_n};
|
use nom::bytes::complete::{tag, take_while_m_n};
|
||||||
use nom::character::complete::{alphanumeric0, digit1, multispace0, multispace1};
|
use nom::character::complete::{alphanumeric1, char, digit1, multispace0, multispace1};
|
||||||
use nom::combinator::{cut, map, map_res};
|
use nom::combinator::{cut, map, map_res, opt};
|
||||||
use nom::error::{context, convert_error, ParseError, VerboseError};
|
use nom::error::{context, convert_error, ParseError, VerboseError};
|
||||||
use nom::multi::separated_list;
|
use nom::multi::{many0, separated_list};
|
||||||
use nom::sequence::{preceded, terminated};
|
use nom::sequence::{delimited, preceded, separated_pair, terminated, tuple};
|
||||||
use nom::IResult;
|
use nom::IResult;
|
||||||
|
|
||||||
use super::{DebuggerError, DebuggerResult};
|
use super::{DebuggerError, DebuggerResult};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Argument {
|
pub enum Value {
|
||||||
Num(u32),
|
Num(u32),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
|
Name(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Argument {
|
#[derive(Debug, PartialEq)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
pub enum Expr {
|
||||||
match self {
|
/// (command-name arg0 arg1 ...)
|
||||||
Argument::Num(_) => write!(f, "number"),
|
Command(Value, Vec<Value>),
|
||||||
Argument::Boolean(_) => write!(f, "boolean"),
|
/// constant = value
|
||||||
}
|
Assignment(Value, Value),
|
||||||
}
|
Empty,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ParsedLine {
|
|
||||||
pub command: String,
|
|
||||||
pub args: Vec<Argument>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_u32_hex<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, u32, E> {
|
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)
|
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> {
|
fn parse_num<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> {
|
||||||
map(alt((parse_u32_hex, parse_u32)), |n| Argument::Num(n))(i)
|
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(
|
context(
|
||||||
"bool",
|
"bool",
|
||||||
alt((
|
alt((
|
||||||
map(tag("true"), |_| Argument::Boolean(true)),
|
map(tag("true"), |_| Value::Boolean(true)),
|
||||||
map(tag("false"), |_| Argument::Boolean(false)),
|
map(tag("false"), |_| Value::Boolean(false)),
|
||||||
)),
|
)),
|
||||||
)(i)
|
)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_argument<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Argument, E> {
|
fn parse_name<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> {
|
||||||
context("argument", alt((parse_num, parse_boolean)))(i)
|
map(alphanumeric1, |s: &str| Value::Name(String::from(s)))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_argument_list<'a, E: ParseError<&'a str>>(
|
fn parse_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> {
|
||||||
i: &'a str,
|
context("argument", alt((parse_boolean, parse_num, parse_name)))(i)
|
||||||
) -> IResult<&'a str, Vec<Argument>, E> {
|
}
|
||||||
|
|
||||||
|
fn parse_command<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Expr, E> {
|
||||||
context(
|
context(
|
||||||
"argument list",
|
"command",
|
||||||
preceded(multispace0, terminated(separated_list(multispace1, parse_argument), multispace0))
|
map(
|
||||||
|
tuple((
|
||||||
|
terminated(parse_name, multispace0),
|
||||||
|
separated_list(multispace1, parse_value),
|
||||||
|
)),
|
||||||
|
|(cmd, args)| Expr::Command(cmd, args),
|
||||||
|
),
|
||||||
)(i)
|
)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_command_name<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
|
fn parse_assignment<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Expr, E> {
|
||||||
context("command", preceded(multispace0, cut(alphanumeric0)))(i)
|
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> {
|
fn _parse_expr<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Expr, E> {
|
||||||
let (i, command_name) = parse_command_name(i)?;
|
context(
|
||||||
println!("command name = {:?}, remaining {:?}", command_name, i);
|
"expression",
|
||||||
let (i, arguments) = parse_argument_list(i)?;
|
preceded(
|
||||||
println!("command: {} arguments: {:?}", command_name, arguments);
|
multispace0,
|
||||||
|
alt((
|
||||||
Ok((
|
parse_assignment,
|
||||||
i,
|
parse_command,
|
||||||
ParsedLine {
|
map(multispace0, |_| Expr::Empty),
|
||||||
command: command_name.to_string(),
|
)),
|
||||||
args: arguments,
|
),
|
||||||
},
|
)(i)
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_command_line(input: &str) -> DebuggerResult<ParsedLine> {
|
pub fn parse_expr(i: &str) -> DebuggerResult<Expr> {
|
||||||
match parse_line::<VerboseError<&str>>(input) {
|
match _parse_expr::<VerboseError<&str>>(i) {
|
||||||
Ok((_, line)) => Ok(line),
|
Ok((_, expr)) => Ok(expr),
|
||||||
Err(nom::Err::Failure(e)) | Err(nom::Err::Error(e)) => {
|
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"),
|
_ => panic!("unhandled parser error"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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())
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Reference in a new issue