Moved line parsing into it's own module and renamed it's types to be preprended with Line.

This commit is contained in:
Filipe Rodrigues 2021-04-26 11:53:26 +01:00
parent 17dfe03800
commit 88cb7ed679
21 changed files with 557 additions and 1173 deletions

View File

@ -22,7 +22,7 @@ pub use size::InstSize;
pub use target::InstTarget;
// Imports
use self::{basic::Decodable as _, pseudo::Decodable as _};
use self::{basic::Decodable as _, parse::LineArg, pseudo::Decodable as _};
use crate::{DataTable, FuncTable, Pos};
use std::{borrow::Borrow, convert::TryInto, ops::Deref};
@ -96,639 +96,6 @@ impl<'a> Inst<'a> {
Directive::decode(pos, bytes).map(Self::Directive).ok_or(DecodeError::NoBytes)
}
}
/*
/// Writes this instruction
pub fn write(&self, f: &mut impl Write) -> Result<(), io::Error> {
match self {
Inst::Basic(inst) => {
f.write_all(&inst.encode().to_le_bytes())?;
},
Inst::Pseudo(inst) => {
for inst in inst.encode() {
f.write_all(&inst.encode().to_le_bytes())?;
}
},
Inst::Directive(directive) => directive.write(f)?,
}
Ok(())
}
/// Get an instruction's size by it's parsed form and position
///
/// Note: This function might not report errors with `inst`, if they happen, such
/// as wrong number of arguments, unless necessary to get it's size.
#[allow(clippy::too_many_lines)] // TODO: Refactor?
#[allow(clippy::match_same_arms)] // Too much work to refactor more currently
pub fn size_from_parsed(inst: &'a Inst, _pos: Pos) -> Result<u32, ParseError> {
let mnemonic = inst.mnemonic.as_str();
let args = inst.args.as_slice();
let inst_size = match (mnemonic, args) {
("dw", _) => 4,
("dh", _) => 2,
("db", _) => 1,
(".ascii", [parse::Arg::String(ref s)]) => (s.len() + (4 - s.len() % 4)).try_into()?,
("nop", [parse::Arg::Literal(len)]) => (4 * len).try_into()?,
("nop", []) => 4,
("li", [_, parse::Arg::Literal(value)]) => match (u16::try_from(*value), i16::try_from(*value)) {
(Ok(_), _) | (_, Ok(_)) => 4,
_ => 8,
},
("la", _) => 8,
(
"sb" | "sh" | "swl" | "sw" | "swr" | "lb" | "lh" | "lwl" | "lw" | "lbu" | "lhu" | "lwr",
[parse::Arg::Register(_), parse::Arg::RegisterOffset { .. }],
) => 4,
(
"sb" | "sh" | "swl" | "sw" | "swr" | "lb" | "lh" | "lwl" | "lw" | "lbu" | "lhu" | "lwr",
[parse::Arg::Register(_), parse::Arg::Literal(_) | parse::Arg::Label(_) | parse::Arg::LabelOffset { .. }],
) => 8,
// Jump immediate
(
"move" | "addi" | "addiu" | "slti" | "sltiu" | "andi" | "ori" | "xori" | "add" | "addu" | "sub" | "subu" | "and" | "or" | "xor" |
"nor" | "slt" | "sltu" | "sll" | "srl" | "sra" | "sllv" | "srlv" | "srav" | "j" | "jal" | "jr" | "jalr" | "b" | "beqz" | "beq" |
"bnez" | "bne" | "blez" | "bgtz" | "bltz" | "bgez" | "bltzal" | "bgezal" | "lui" | "cop0" | "cop1" | "cop2" | "cop3" | "mfc0" |
"mfc1" | "mfc2" | "mfc3" | "cfc0" | "cfc1" | "cfc2" | "cfc3" | "mtc0" | "mtc1" | "mtc2" | "mtc3" | "ctc0" | "ctc1" | "ctc2" |
"ctc3" | "lwc0" | "lwc1" | "lwc2" | "lwc3" | "swc0" | "swc1" | "swc2" | "swc3" | "mflo" | "mfhi" | "mtlo" | "mthi" | "mult" |
"multu" | "div" | "divu" | "break" | "sys",
_,
) => 4,
_ => return Err(ParseError::UnknownMnemonic),
};
Ok(inst_size)
}
/// Creates an instruction from a parsed instruction
#[allow(clippy::too_many_lines)] // TODO: Refactor?
pub fn from_parsed(inst: &'a Inst, pos: Pos, labels_by_name: &HashMap<LabelName, Pos>) -> Result<Self, ParseError> {
let mnemonic = inst.mnemonic.as_str();
let args = inst.args.as_slice();
// Helper that converts a label to a target
let label_to_target = |label: &str| {
labels_by_name
.get(label)
.copied()
.ok_or_else(|| ParseError::UnknownLabel(label.to_owned()))
};
// Helper that converts a label to an offset
let label_to_offset = |label: &str, offset: i64| -> Result<i16, ParseError> {
label_to_target(label)?
.sub(pos)
.add(offset)
.div(4)
.sub(1)
.try_into()
.map_err(ParseError::RelativeJumpTooFar)
};
let inst = match mnemonic {
// Directives
"dw" | "dh" | "db" | ".ascii" => {
// Get the argument, we only support single arguments
let arg: &'a parse::Arg = match args {
[arg] => arg,
_ => return Err(ParseError::InvalidArguments),
};
// Then get the directive itself
// TODO: Allow `dw`s to have negative numbers by casting them?
let directive = match mnemonic {
"dw" => match arg {
// If it's a label, get the label's address
parse::Arg::Label(label) => labels_by_name
.get(label)
.map(|&Pos(pos)| Directive::Dw(pos))
.ok_or_else(|| ParseError::UnknownLabel(label.clone()))?,
parse::Arg::LabelOffset { label, offset } => labels_by_name
.get(label)
.map(|pos| Ok::<_, ParseError>(pos + u32::try_from(*offset)?))
.ok_or_else(|| ParseError::UnknownLabel(label.clone()))?
.map(|Pos(pos)| Directive::Dw(pos))?,
&parse::Arg::Literal(value) => Directive::Dw(value.try_into()?),
_ => return Err(ParseError::InvalidArguments),
},
"dh" => Directive::Dh(arg.as_literal().ok_or(ParseError::InvalidArguments)?.try_into()?),
"db" => Directive::Db(arg.as_literal().ok_or(ParseError::InvalidArguments)?.try_into()?),
".ascii" => arg
.as_string()
.map(AsciiStr::from_ascii)
.ok_or(ParseError::InvalidArguments)?
.map(Directive::Ascii)
.map_err(ParseError::StringNonAscii)?,
_ => unreachable!(),
};
// And return it
Self::Directive(directive)
},
// Nop
"nop" => match *args {
[parse::Arg::Literal(len)] => Self::Pseudo(pseudo::Inst::Nop(pseudo::nop::Inst { len: len.try_into()? })),
[] => Self::Pseudo(pseudo::Inst::Nop(pseudo::nop::Inst { len: 1 })),
_ => return Err(ParseError::InvalidArguments),
},
// Move
"move" => match *args {
[parse::Arg::Register(dst), parse::Arg::Register(src)] => Self::Pseudo(pseudo::Inst::MoveReg(pseudo::move_reg::Inst { dst, src })),
_ => return Err(ParseError::InvalidArguments),
},
// Load immediate
"li" => {
// Note: No labels for `li`
let (reg, value) = match *args {
[parse::Arg::Register(reg), parse::Arg::Literal(value)] => (reg, value),
_ => return Err(ParseError::InvalidArguments),
};
// Try to convert it to a `i16`, then `u16`, then `u32`.
// Note: It seems it is preferred to try `i16` first.
let kind = if let Ok(value) = value.try_into() {
pseudo::load_imm::Kind::HalfWordSigned(value)
} else if let Ok(value) = value.try_into() {
pseudo::load_imm::Kind::HalfWordUnsigned(value)
} else {
pseudo::load_imm::Kind::Word(value.try_into()?)
};
Self::Pseudo(pseudo::Inst::LoadImm(pseudo::load_imm::Inst { dst: reg, kind }))
},
// Load address
"la" => {
let (dst, target) = match *args {
[parse::Arg::Register(dst), parse::Arg::Literal(value)] => (dst, Pos(value.try_into()?)),
[parse::Arg::Register(dst), parse::Arg::Label(ref label)] => (dst, label_to_target(label)?),
[parse::Arg::Register(dst), parse::Arg::LabelOffset { ref label, offset }] => {
(dst, label_to_target(label)? + i32::try_from(offset)?)
},
_ => return Err(ParseError::InvalidArguments),
};
Self::Pseudo(pseudo::Inst::LoadImm(pseudo::load_imm::Inst {
dst,
kind: pseudo::load_imm::Kind::Address(target),
}))
},
// Alu Immediate
"addi" | "addiu" | "slti" | "sltiu" | "andi" | "ori" | "xori" => {
let (reg1, reg2, lit) = match *args {
[parse::Arg::Register(reg), parse::Arg::Literal(lit)] => (reg, reg, lit),
[parse::Arg::Register(reg1), parse::Arg::Register(reg2), parse::Arg::Literal(lit)] => (reg1, reg2, lit),
_ => return Err(ParseError::InvalidArguments),
};
Self::Basic(basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
dst: reg1,
lhs: reg2,
kind: match mnemonic {
"addi" => basic::alu::imm::Kind::Add(lit.try_into()?),
"addiu" => basic::alu::imm::Kind::AddUnsigned(lit.try_into()?),
"slti" => basic::alu::imm::Kind::SetLessThan(lit.try_into()?),
"sltiu" => basic::alu::imm::Kind::SetLessThanUnsigned(lit.try_into()?),
"andi" => basic::alu::imm::Kind::And(lit.try_into()?),
"ori" => basic::alu::imm::Kind::Or(lit.try_into()?),
"xori" => basic::alu::imm::Kind::Xor(lit.try_into()?),
_ => unreachable!(),
},
})))
},
// Alu register
"add" | "addu" | "sub" | "subu" | "and" | "or" | "xor" | "nor" | "slt" | "sltu" => {
let (reg1, reg2, reg3) = match *args {
[parse::Arg::Register(reg1), parse::Arg::Register(reg2)] => (reg1, reg1, reg2),
[parse::Arg::Register(reg1), parse::Arg::Register(reg2), parse::Arg::Register(reg3)] => (reg1, reg2, reg3),
_ => return Err(ParseError::InvalidArguments),
};
Self::Basic(basic::Inst::Alu(basic::alu::Inst::Reg(basic::alu::reg::Inst {
dst: reg1,
lhs: reg2,
rhs: reg3,
kind: match mnemonic {
"add" => basic::alu::reg::Kind::Add,
"addu" => basic::alu::reg::Kind::AddUnsigned,
"sub" => basic::alu::reg::Kind::Sub,
"subu" => basic::alu::reg::Kind::SubUnsigned,
"and" => basic::alu::reg::Kind::And,
"or" => basic::alu::reg::Kind::Or,
"xor" => basic::alu::reg::Kind::Xor,
"nor" => basic::alu::reg::Kind::Nor,
"slt" => basic::alu::reg::Kind::SetLessThan,
"sltu" => basic::alu::reg::Kind::SetLessThanUnsigned,
_ => unreachable!(),
},
})))
},
// Shift Immediate
"sll" | "srl" | "sra" => {
let (reg1, reg2, lit) = match *args {
[parse::Arg::Register(reg), parse::Arg::Literal(lit)] => (reg, reg, lit),
[parse::Arg::Register(reg1), parse::Arg::Register(reg2), parse::Arg::Literal(lit)] => (reg1, reg2, lit),
_ => return Err(ParseError::InvalidArguments),
};
Self::Basic(basic::Inst::Shift(basic::shift::Inst::Imm(basic::shift::imm::Inst {
dst: reg1,
lhs: reg2,
rhs: lit.try_into()?,
kind: match mnemonic {
"sll" => basic::shift::imm::Kind::LeftLogical,
"srl" => basic::shift::imm::Kind::RightLogical,
"sra" => basic::shift::imm::Kind::RightArithmetic,
_ => unreachable!(),
},
})))
},
// Shift register
"sllv" | "srlv" | "srav" => {
let (reg1, reg2, reg3) = match *args {
[parse::Arg::Register(reg1), parse::Arg::Register(reg2)] => (reg1, reg1, reg2),
[parse::Arg::Register(reg1), parse::Arg::Register(reg2), parse::Arg::Register(reg3)] => (reg1, reg2, reg3),
_ => return Err(ParseError::InvalidArguments),
};
Self::Basic(basic::Inst::Shift(basic::shift::Inst::Reg(basic::shift::reg::Inst {
dst: reg1,
lhs: reg2,
rhs: reg3,
kind: match mnemonic {
"sllv" => basic::shift::reg::Kind::LeftLogical,
"srlv" => basic::shift::reg::Kind::RightLogical,
"srav" => basic::shift::reg::Kind::RightArithmetic,
_ => unreachable!(),
},
})))
},
// Store / Load
"sb" | "sh" | "swl" | "sw" | "swr" | "lb" | "lh" | "lwl" | "lw" | "lbu" | "lhu" | "lwr" => {
let (reg1, reg2_offset, target) = match *args {
[parse::Arg::Register(reg1), parse::Arg::RegisterOffset { register: reg2, offset }] => {
(reg1, Some((reg2, offset.try_into()?)), None)
},
[parse::Arg::Register(reg), parse::Arg::Literal(pos)] => (reg, None, Some(Pos(pos.try_into()?))),
[parse::Arg::Register(reg), parse::Arg::Label(ref label)] => (reg, None, Some(label_to_target(label)?)),
[parse::Arg::Register(reg), parse::Arg::LabelOffset { ref label, offset }] => {
(reg, None, Some(label_to_target(label)? + i32::try_from(offset)?))
},
_ => return Err(ParseError::InvalidArguments),
};
match (mnemonic, reg2_offset, target) {
("sb" | "sh" | "swl" | "sw" | "swr", Some((reg2, offset)), None) => Self::Basic(basic::Inst::Store(basic::store::Inst {
value: reg1,
addr: reg2,
offset,
kind: match mnemonic {
"sb" => basic::store::Kind::Byte,
"sh" => basic::store::Kind::HalfWord,
"swl" => basic::store::Kind::WordLeft,
"sw" => basic::store::Kind::Word,
"swr" => basic::store::Kind::WordRight,
_ => unreachable!(),
},
})),
("sb" | "sh" | "swl" | "sw" | "swr", None, Some(target)) => Self::Pseudo(pseudo::Inst::Store(pseudo::store::Inst {
value: reg1,
target,
kind: match mnemonic {
"sb" => basic::store::Kind::Byte,
"sh" => basic::store::Kind::HalfWord,
"swl" => basic::store::Kind::WordLeft,
"sw" => basic::store::Kind::Word,
"swr" => basic::store::Kind::WordRight,
_ => unreachable!(),
},
})),
("lb" | "lh" | "lwl" | "lw" | "lbu" | "lhu" | "lwr", Some((reg2, offset)), None) => {
Self::Basic(basic::Inst::Load(basic::load::Inst {
value: reg1,
addr: reg2,
offset,
kind: match mnemonic {
"lb" => basic::load::Kind::Byte,
"lh" => basic::load::Kind::HalfWord,
"lwl" => basic::load::Kind::WordLeft,
"lw" => basic::load::Kind::Word,
"lbu" => basic::load::Kind::ByteUnsigned,
"lhu" => basic::load::Kind::HalfWordUnsigned,
"lwr" => basic::load::Kind::WordRight,
_ => unreachable!(),
},
}))
},
("lb" | "lh" | "lwl" | "lw" | "lbu" | "lhu" | "lwr", None, Some(target)) => {
Self::Pseudo(pseudo::Inst::Load(pseudo::load::Inst {
value: reg1,
target,
kind: match mnemonic {
"lb" => basic::load::Kind::Byte,
"lh" => basic::load::Kind::HalfWord,
"lwl" => basic::load::Kind::WordLeft,
"lw" => basic::load::Kind::Word,
"lbu" => basic::load::Kind::ByteUnsigned,
"lhu" => basic::load::Kind::HalfWordUnsigned,
"lwr" => basic::load::Kind::WordRight,
_ => unreachable!(),
},
}))
},
_ => unreachable!(),
}
},
// Jump immediate
"j" | "jal" => {
let target = match *args {
[parse::Arg::Literal(pos)] => Pos(pos.try_into()?),
[parse::Arg::Label(ref label)] => label_to_target(label)?,
[parse::Arg::LabelOffset { ref label, offset }] => label_to_target(label)? + i32::try_from(offset)?,
_ => return Err(ParseError::InvalidArguments),
};
Self::Basic(basic::Inst::Jmp(basic::jmp::Inst::Imm(basic::jmp::imm::Inst {
imm: (target.0 & 0x0fffffff) / 4,
kind: match mnemonic {
"j" => basic::jmp::imm::Kind::Jump,
"jal" => basic::jmp::imm::Kind::JumpLink,
_ => unreachable!(),
},
})))
},
// Jump register
"jr" | "jalr" => {
let (target, link) = match *args {
[parse::Arg::Register(target)] => (target, None),
[parse::Arg::Register(target), parse::Arg::Register(link)] => (target, Some(link)),
_ => return Err(ParseError::InvalidArguments),
};
Self::Basic(basic::Inst::Jmp(basic::jmp::Inst::Reg(basic::jmp::reg::Inst {
target,
kind: match (mnemonic, link) {
("jr", None) => basic::jmp::reg::Kind::Jump,
("jalr", None) => basic::jmp::reg::Kind::JumpLink(Register::Ra),
("jalr", Some(link)) => basic::jmp::reg::Kind::JumpLink(link),
_ => return Err(ParseError::InvalidArguments),
},
})))
},
// Conditionals
"b" | "beqz" | "beq" | "bnez" | "bne" | "blez" | "bgtz" | "bltz" | "bgez" | "bltzal" | "bgezal" => {
// Get all args
// Note: Literals are absolute
let (reg1, reg2, offset) = match *args {
// <reg1> <reg2> <target>
[parse::Arg::Register(reg1), parse::Arg::Register(reg2), parse::Arg::Literal(target)] => (
Some(reg1),
Some(reg2),
u32::try_from(target)?.wrapping_sub(pos.0).as_signed().div(4i32).sub(1i32).try_into()?,
),
[parse::Arg::Register(reg1), parse::Arg::Register(reg2), parse::Arg::Label(ref label)] => {
(Some(reg1), Some(reg2), label_to_offset(label, 0)?)
},
[parse::Arg::Register(reg1), parse::Arg::Register(reg2), parse::Arg::LabelOffset { ref label, offset }] => {
(Some(reg1), Some(reg2), label_to_offset(label, offset)?)
},
// <reg> <target>
[parse::Arg::Register(reg1), parse::Arg::Literal(target)] => (
Some(reg1),
None,
u32::try_from(target)?.wrapping_sub(pos.0).as_signed().div(4i32).sub(1i32).try_into()?,
),
[parse::Arg::Register(reg1), parse::Arg::Label(ref label)] => (Some(reg1), None, label_to_offset(label, 0)?),
[parse::Arg::Register(reg1), parse::Arg::LabelOffset { ref label, offset }] => {
(Some(reg1), None, label_to_offset(label, offset)?)
},
// <target>
[parse::Arg::Literal(target)] => (
None,
None,
u32::try_from(target)?.wrapping_sub(pos.0).as_signed().div(4i32).sub(1i32).try_into()?,
),
[parse::Arg::Label(ref label)] => (None, None, label_to_offset(label, 0)?),
[parse::Arg::LabelOffset { ref label, offset }] => (None, None, label_to_offset(label, offset)?),
_ => return Err(ParseError::InvalidArguments),
};
match (mnemonic, reg1, reg2) {
("b", None, None) => Self::Basic(basic::Inst::Cond(basic::cond::Inst {
arg: Register::Zr,
offset,
kind: basic::cond::Kind::Equal(Register::Zr),
})),
("beqz", Some(arg), None) => Self::Basic(basic::Inst::Cond(basic::cond::Inst {
arg,
offset,
kind: basic::cond::Kind::Equal(Register::Zr),
})),
("bnez", Some(arg), None) => Self::Basic(basic::Inst::Cond(basic::cond::Inst {
arg,
offset,
kind: basic::cond::Kind::NotEqual(Register::Zr),
})),
("beq", Some(arg), Some(other)) => Self::Basic(basic::Inst::Cond(basic::cond::Inst {
arg,
offset,
kind: basic::cond::Kind::Equal(other),
})),
("bne", Some(arg), Some(other)) => Self::Basic(basic::Inst::Cond(basic::cond::Inst {
arg,
offset,
kind: basic::cond::Kind::NotEqual(other),
})),
("blez", Some(arg), None) => Self::Basic(basic::Inst::Cond(basic::cond::Inst {
arg,
offset,
kind: basic::cond::Kind::LessOrEqualZero,
})),
("bgtz", Some(arg), None) => Self::Basic(basic::Inst::Cond(basic::cond::Inst {
arg,
offset,
kind: basic::cond::Kind::GreaterThanZero,
})),
("bltz", Some(arg), None) => Self::Basic(basic::Inst::Cond(basic::cond::Inst {
arg,
offset,
kind: basic::cond::Kind::LessThanZero,
})),
("bgez", Some(arg), None) => Self::Basic(basic::Inst::Cond(basic::cond::Inst {
arg,
offset,
kind: basic::cond::Kind::GreaterOrEqualZero,
})),
("bltzal", Some(arg), None) => Self::Basic(basic::Inst::Cond(basic::cond::Inst {
arg,
offset,
kind: basic::cond::Kind::LessThanZeroLink,
})),
("bgezal", Some(arg), None) => Self::Basic(basic::Inst::Cond(basic::cond::Inst {
arg,
offset,
kind: basic::cond::Kind::GreaterOrEqualZeroLink,
})),
(_, None, Some(_)) => unreachable!(),
_ => return Err(ParseError::InvalidArguments),
}
},
// Lui
"lui" => match *args {
[parse::Arg::Register(dst), parse::Arg::Literal(value)] => Self::Basic(basic::Inst::Lui(basic::lui::Inst {
dst,
value: value.try_into()?,
})),
_ => return Err(ParseError::InvalidArguments),
},
// Co-processor
"cop0" | "cop1" | "cop2" | "cop3" => {
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
let imm = match *args {
[parse::Arg::Literal(imm)] => imm.try_into()?,
_ => return Err(ParseError::InvalidArguments),
};
Self::Basic(basic::Inst::Co(basic::co::Inst {
n,
kind: basic::co::Kind::CopN { imm },
}))
},
"mfc0" | "mfc1" | "mfc2" | "mfc3" | "cfc0" | "cfc1" | "cfc2" | "cfc3" | "mtc0" | "mtc1" | "mtc2" | "mtc3" | "ctc0" | "ctc1" |
"ctc2" | "ctc3" => {
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
let (reg, imm) = match *args {
[parse::Arg::Register(dst), parse::Arg::Literal(src)] => (dst, src.try_into()?),
_ => return Err(ParseError::InvalidArguments),
};
let kind = match &mnemonic[0..=0] {
"m" => basic::co::RegisterKind::Data,
"c" => basic::co::RegisterKind::Control,
_ => unreachable!(),
};
match &mnemonic[1..=1] {
"f" => Self::Basic(basic::Inst::Co(basic::co::Inst {
n,
kind: basic::co::Kind::MoveFrom { dst: reg, src: imm, kind },
})),
"t" => Self::Basic(basic::Inst::Co(basic::co::Inst {
n,
kind: basic::co::Kind::MoveTo { dst: imm, src: reg, kind },
})),
_ => unreachable!(),
}
},
"lwc0" | "lwc1" | "lwc2" | "lwc3" | "swc0" | "swc1" | "swc2" | "swc3" => {
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
let (dst, src, offset) = match *args {
[parse::Arg::Literal(dst), parse::Arg::RegisterOffset { register: src, offset }] => (dst.try_into()?, src, offset.try_into()?),
_ => return Err(ParseError::InvalidArguments),
};
match &mnemonic[0..=0] {
"l" => Self::Basic(basic::Inst::Co(basic::co::Inst {
n,
kind: basic::co::Kind::Load { dst, src, offset },
})),
"s" => Self::Basic(basic::Inst::Co(basic::co::Inst {
n,
kind: basic::co::Kind::Store { dst, src, offset },
})),
_ => unreachable!(),
}
},
// Mult move
"mflo" | "mfhi" | "mtlo" | "mthi" => {
let reg = match *args {
[parse::Arg::Register(reg)] => reg,
_ => return Err(ParseError::InvalidArguments),
};
let mult_reg = match &mnemonic[2..=3] {
"lo" => basic::mult::MultReg::Lo,
"hi" => basic::mult::MultReg::Hi,
_ => unreachable!(),
};
match &mnemonic[1..=1] {
"f" => Self::Basic(basic::Inst::Mult(basic::mult::Inst::MoveFrom { dst: reg, src: mult_reg })),
"t" => Self::Basic(basic::Inst::Mult(basic::mult::Inst::MoveTo { dst: mult_reg, src: reg })),
_ => unreachable!(),
}
},
// Mult / Div
"mult" | "multu" | "div" | "divu" => {
let (lhs, rhs) = match *args {
[parse::Arg::Register(lhs), parse::Arg::Register(rhs)] => (lhs, rhs),
_ => return Err(ParseError::InvalidArguments),
};
Self::Basic(basic::Inst::Mult(basic::mult::Inst::Mult {
lhs,
rhs,
mode: match mnemonic {
"divu" | "multu" => basic::mult::MultMode::Unsigned,
"div" | "mult" => basic::mult::MultMode::Signed,
_ => unreachable!(),
},
kind: match mnemonic {
"mult" | "multu" => basic::mult::MultKind::Mult,
"div" | "divu" => basic::mult::MultKind::Div,
_ => unreachable!(),
},
}))
},
// Syscalls
"break" | "sys" => {
let comment = match *args {
[parse::Arg::Literal(comment)] => comment.try_into()?,
_ => return Err(ParseError::InvalidArguments),
};
Self::Basic(basic::Inst::Sys(basic::sys::Inst {
comment,
kind: match mnemonic {
"break" => basic::sys::Kind::Break,
"sys" => basic::sys::Kind::Sys,
_ => return Err(ParseError::InvalidArguments),
},
}))
},
_ => return Err(ParseError::UnknownMnemonic),
};
Ok(inst)
}
}
*/
impl<'a> InstSize for Inst<'a> {
fn size(&self) -> usize {
@ -759,20 +126,20 @@ pub trait ParseCtx {
fn label_pos(&self, label: &str) -> Option<Pos>;
/// Retrieves a position from an argument
fn arg_pos(&self, arg: &parse::Arg) -> Result<Pos, basic::ParseError> {
fn arg_pos(&self, arg: &LineArg) -> Result<Pos, basic::ParseError> {
match *arg {
parse::Arg::Literal(pos) => pos.try_into().map(Pos).map_err(|_| basic::ParseError::LiteralOutOfRange),
parse::Arg::Label(ref label) => self.label_pos(label).ok_or(basic::ParseError::UnknownLabel),
LineArg::Literal(pos) => pos.try_into().map(Pos).map_err(|_| basic::ParseError::LiteralOutOfRange),
LineArg::Label(ref label) => self.label_pos(label).ok_or(basic::ParseError::UnknownLabel),
_ => Err(basic::ParseError::InvalidArguments),
}
}
/// Retrieves a position and offset from an argument
fn arg_pos_offset(&self, arg: &parse::Arg) -> Result<(Pos, i64), basic::ParseError> {
fn arg_pos_offset(&self, arg: &LineArg) -> Result<(Pos, i64), basic::ParseError> {
match *arg {
parse::Arg::Literal(pos) => pos.try_into().map(|pos| (Pos(pos), 0)).map_err(|_| basic::ParseError::LiteralOutOfRange),
parse::Arg::Label(ref label) => self.label_pos(label).map(|pos| (pos, 0)).ok_or(basic::ParseError::UnknownLabel),
parse::Arg::LabelOffset { ref label, offset } => self.label_pos(label).map(|pos| (pos, offset)).ok_or(basic::ParseError::UnknownLabel),
LineArg::Literal(pos) => pos.try_into().map(|pos| (Pos(pos), 0)).map_err(|_| basic::ParseError::LiteralOutOfRange),
LineArg::Label(ref label) => self.label_pos(label).map(|pos| (pos, 0)).ok_or(basic::ParseError::UnknownLabel),
LineArg::LabelOffset { ref label, offset } => self.label_pos(label).map(|pos| (pos, offset)).ok_or(basic::ParseError::UnknownLabel),
_ => Err(basic::ParseError::InvalidArguments),
}
}

View File

@ -17,7 +17,7 @@ pub mod store;
pub mod sys;
// Imports
use super::{parse, InstSize, ParseCtx, Register};
use super::{parse::LineArg, InstSize, ParseCtx, Register};
use crate::inst::InstFmt;
/// All basic instructions
@ -94,7 +94,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], ctx: &Ctx) -> Result<Self, ParseError> {
#[rustfmt::skip]
let parsers: &[&dyn Fn() -> Result<Self, ParseError>] = &[
&|| alu ::Inst::parse(mnemonic, args, ctx).map(Self::Alu ),
@ -214,7 +214,7 @@ pub enum ParseError {
/// Instruction parsing
pub trait Parsable: Sized {
/// Parses this instruction
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], ctx: &Ctx) -> Result<Self, ParseError>;
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], ctx: &Ctx) -> Result<Self, ParseError>;
}
/// Register modifying instructions

View File

@ -8,7 +8,8 @@ pub mod reg;
use super::{ModifiesReg, Parsable, ParseError};
use crate::inst::{
basic::{Decodable, Encodable},
parse, InstFmt, ParseCtx,
parse::LineArg,
InstFmt, ParseCtx,
};
/// Alu register instructions
@ -41,7 +42,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], ctx: &Ctx) -> Result<Self, ParseError> {
match imm::Inst::parse(mnemonic, args, ctx) {
Ok(inst) => Ok(Self::Imm(inst)),
Err(ParseError::UnknownMnemonic) => reg::Inst::parse(mnemonic, args, ctx).map(Self::Reg),

View File

@ -3,11 +3,10 @@
// Imports
use crate::inst::{
basic::{Decodable, Encodable, ModifiesReg, Parsable, ParseError},
parse, InstFmt, ParseCtx, Register,
parse::LineArg, InstFmt, ParseCtx, Register,
};
use dcb_util::SignedHex;
use int_conv::{Signed, Truncated, ZeroExtended};
use std::{convert::TryInto, fmt};
use int_conv::{Signed, Truncated, ZeroExtended};use std::{convert::TryInto, fmt};
/// Instruction kind
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
@ -123,7 +122,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], _ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], _ctx: &Ctx) -> Result<Self, ParseError> {
#[rustfmt::skip]
let to_kind = match mnemonic {
"addi" => |value: i64| value.try_into().map(Kind::Add ),
@ -138,15 +137,16 @@ impl Parsable for Inst {
match *args {
// Disallow `slti` and `sltiu` in short form
[parse::Arg::Register(_), parse::Arg::Literal(_)] if ["slti", "sltiu"].contains(&mnemonic) => Err(ParseError::InvalidArguments),
[LineArg::Register(_), LineArg::Literal(_)] if ["slti", "sltiu"].contains(&mnemonic) => Err(ParseError::InvalidArguments),
// Else parse both `$dst, $lhs, value` and `$dst, value`.
[parse::Arg::Register(lhs @ dst), parse::Arg::Literal(value)] |
[parse::Arg::Register(dst), parse::Arg::Register(lhs), parse::Arg::Literal(value)] => Ok(Self {
dst,
lhs,
kind: to_kind(value).map_err(|_| ParseError::LiteralOutOfRange)?,
}),
[LineArg::Register(lhs @ dst), LineArg::Literal(value)] | [LineArg::Register(dst), LineArg::Register(lhs), LineArg::Literal(value)] => {
Ok(Self {
dst,
lhs,
kind: to_kind(value).map_err(|_| ParseError::LiteralOutOfRange)?,
})
},
_ => Err(ParseError::InvalidArguments),
}
}

View File

@ -3,7 +3,7 @@
// Imports
use crate::inst::{
basic::{Decodable, Encodable, ModifiesReg, Parsable, ParseError},
parse, InstFmt, ParseCtx, Register,
parse::LineArg, InstFmt, ParseCtx, Register,
};
/// Alu register instruction kind
@ -132,7 +132,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], _ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], _ctx: &Ctx) -> Result<Self, ParseError> {
#[rustfmt::skip]
let kind = match mnemonic {
"add" => Kind::Add ,
@ -150,11 +150,11 @@ impl Parsable for Inst {
match *args {
// Disallow `slt` and `sltu` in short form
[parse::Arg::Register(_), parse::Arg::Register(_)] if ["slt", "sltu"].contains(&mnemonic) => Err(ParseError::InvalidArguments),
[LineArg::Register(_), LineArg::Register(_)] if ["slt", "sltu"].contains(&mnemonic) => Err(ParseError::InvalidArguments),
// Else parse both `$dst, $lhs, $rhs` and `$dst, $rhs`.
[parse::Arg::Register(lhs @ dst), parse::Arg::Register(rhs)] |
[parse::Arg::Register(dst), parse::Arg::Register(lhs), parse::Arg::Register(rhs)] => Ok(Self { dst, lhs, rhs, kind }),
[LineArg::Register(lhs @ dst), LineArg::Register(rhs)] |
[LineArg::Register(dst), LineArg::Register(lhs), LineArg::Register(rhs)] => Ok(Self { dst, lhs, rhs, kind }),
_ => Err(ParseError::InvalidArguments),
}
}

View File

@ -4,7 +4,8 @@
use super::{ModifiesReg, Parsable, ParseError};
use crate::inst::{
basic::{Decodable, Encodable},
parse, InstFmt, ParseCtx, Register,
parse::LineArg,
InstFmt, ParseCtx, Register,
};
use dcb_util::SignedHex;
use int_conv::{Signed, Truncated, ZeroExtended};
@ -170,12 +171,12 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], _ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], _ctx: &Ctx) -> Result<Self, ParseError> {
let inst = match mnemonic {
"cop0" | "cop1" | "cop2" | "cop3" => {
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
let imm = match *args {
[parse::Arg::Literal(imm)] => imm.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
[LineArg::Literal(imm)] => imm.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
_ => return Err(ParseError::InvalidArguments),
};
@ -185,7 +186,7 @@ impl Parsable for Inst {
"ctc2" | "ctc3" => {
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
let (reg, imm) = match *args {
[parse::Arg::Register(dst), parse::Arg::Literal(src)] => (dst, src.try_into().map_err(|_| ParseError::LiteralOutOfRange)?),
[LineArg::Register(dst), LineArg::Literal(src)] => (dst, src.try_into().map_err(|_| ParseError::LiteralOutOfRange)?),
_ => return Err(ParseError::InvalidArguments),
};
@ -210,7 +211,7 @@ impl Parsable for Inst {
"lwc0" | "lwc1" | "lwc2" | "lwc3" | "swc0" | "swc1" | "swc2" | "swc3" => {
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
let (dst, src, offset) = match *args {
[parse::Arg::Literal(dst), parse::Arg::RegisterOffset { register: src, offset }] => (
[LineArg::Literal(dst), LineArg::RegisterOffset { register: src, offset }] => (
dst.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
src,
offset.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,

View File

@ -5,12 +5,11 @@ use super::{ModifiesReg, Parsable, ParseError};
use crate::{
inst::{
basic::{Decodable, Encodable},
parse, InstTarget, InstTargetFmt, ParseCtx, Register,
parse::LineArg, InstTarget, InstTargetFmt, ParseCtx, Register,
},
Pos,
};
use int_conv::{SignExtended, Signed, Truncated, ZeroExtended};
use std::{convert::TryInto, fmt};
use int_conv::{SignExtended, Signed, Truncated, ZeroExtended};use std::{convert::TryInto, fmt};
/// Instruction kind
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
@ -116,7 +115,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], ctx: &Ctx) -> Result<Self, ParseError> {
// Note: Literals are absolute, not relative
// Calculates the offset between a position and the current one
@ -140,43 +139,43 @@ impl Parsable for Inst {
_ => return Err(ParseError::InvalidArguments),
},
"beqz" => match *args {
[parse::Arg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::Equal(Register::Zr)),
[LineArg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::Equal(Register::Zr)),
_ => return Err(ParseError::InvalidArguments),
},
"bnez" => match *args {
[parse::Arg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::NotEqual(Register::Zr)),
[LineArg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::NotEqual(Register::Zr)),
_ => return Err(ParseError::InvalidArguments),
},
"beq" => match *args {
[parse::Arg::Register(arg), parse::Arg::Register(reg), ref target] => (arg, target_arg_to_offset(target)?, Kind::Equal(reg)),
[LineArg::Register(arg), LineArg::Register(reg), ref target] => (arg, target_arg_to_offset(target)?, Kind::Equal(reg)),
_ => return Err(ParseError::InvalidArguments),
},
"bne" => match *args {
[parse::Arg::Register(arg), parse::Arg::Register(reg), ref target] => (arg, target_arg_to_offset(target)?, Kind::NotEqual(reg)),
[LineArg::Register(arg), LineArg::Register(reg), ref target] => (arg, target_arg_to_offset(target)?, Kind::NotEqual(reg)),
_ => return Err(ParseError::InvalidArguments),
},
"blez" => match *args {
[parse::Arg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::LessOrEqualZero),
[LineArg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::LessOrEqualZero),
_ => return Err(ParseError::InvalidArguments),
},
"bgtz" => match *args {
[parse::Arg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::GreaterThanZero),
[LineArg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::GreaterThanZero),
_ => return Err(ParseError::InvalidArguments),
},
"bltz" => match *args {
[parse::Arg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::LessThanZero),
[LineArg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::LessThanZero),
_ => return Err(ParseError::InvalidArguments),
},
"bgez" => match *args {
[parse::Arg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::GreaterOrEqualZero),
[LineArg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::GreaterOrEqualZero),
_ => return Err(ParseError::InvalidArguments),
},
"bltzal" => match *args {
[parse::Arg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::LessThanZeroLink),
[LineArg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::LessThanZeroLink),
_ => return Err(ParseError::InvalidArguments),
},
"bgezal" => match *args {
[parse::Arg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::GreaterOrEqualZeroLink),
[LineArg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::GreaterOrEqualZeroLink),
_ => return Err(ParseError::InvalidArguments),
},

View File

@ -8,7 +8,7 @@ pub mod reg;
use super::{ModifiesReg, Parsable, ParseError};
use crate::inst::{
basic::{Decodable, Encodable},
parse, InstFmt, ParseCtx, Register,
parse::LineArg, InstFmt, ParseCtx, Register,
};
/// Jmp register instructions
@ -41,7 +41,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], ctx: &Ctx) -> Result<Self, ParseError> {
match imm::Inst::parse(mnemonic, args, ctx) {
Ok(inst) => Ok(Self::Imm(inst)),
Err(ParseError::UnknownMnemonic) => reg::Inst::parse(mnemonic, args, ctx).map(Self::Reg),

View File

@ -4,7 +4,7 @@
use crate::{
inst::{
basic::{Decodable, Encodable, ModifiesReg, Parsable, ParseError},
parse, InstTarget, InstTargetFmt, ParseCtx, Register,
parse::LineArg, InstTarget, InstTargetFmt, ParseCtx, Register,
},
Pos,
};
@ -83,7 +83,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], ctx: &Ctx) -> Result<Self, ParseError> {
let (pos, kind) = match mnemonic {
"j" => match args {
[arg] => (ctx.arg_pos(arg)?, Kind::Jump),

View File

@ -3,7 +3,8 @@
// Imports
use crate::inst::{
basic::{Decodable, Encodable, ModifiesReg, Parsable, ParseError},
parse, InstFmt, ParseCtx, Register,
parse::LineArg,
InstFmt, ParseCtx, Register,
};
/// Jmp register instruction kind
@ -74,15 +75,15 @@ impl Encodable for Inst {
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], _ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], _ctx: &Ctx) -> Result<Self, ParseError> {
let (target, kind) = match mnemonic {
"jr" => match *args {
[parse::Arg::Register(target)] => (target, Kind::Jump),
[LineArg::Register(target)] => (target, Kind::Jump),
_ => return Err(ParseError::InvalidArguments),
},
"jalr" => match *args {
[parse::Arg::Register(target), parse::Arg::Register(reg)] => (target, Kind::JumpLink(reg)),
[LineArg::Register(target), LineArg::Register(reg)] => (target, Kind::JumpLink(reg)),
_ => return Err(ParseError::InvalidArguments),
},

View File

@ -4,11 +4,11 @@
use super::{ModifiesReg, Parsable, ParseError};
use crate::inst::{
basic::{Decodable, Encodable},
parse, InstFmt, ParseCtx, Register,
parse::LineArg,
InstFmt, ParseCtx, Register,
};
use dcb_util::SignedHex;
use int_conv::{Signed, Truncated, ZeroExtended};
use std::convert::TryInto;
use int_conv::{Signed, Truncated, ZeroExtended};use std::convert::TryInto;
/// Instruction kind
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
@ -117,7 +117,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], _ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], _ctx: &Ctx) -> Result<Self, ParseError> {
let kind = match mnemonic {
"lb" => Kind::Byte,
"lh" => Kind::HalfWord,
@ -130,8 +130,8 @@ impl Parsable for Inst {
};
let (value, addr, offset) = match *args {
[parse::Arg::Register(value), parse::Arg::Register(addr)] => (value, addr, 0),
[parse::Arg::Register(value), parse::Arg::RegisterOffset { register: addr, offset }] => {
[LineArg::Register(value), LineArg::Register(addr)] => (value, addr, 0),
[LineArg::Register(value), LineArg::RegisterOffset { register: addr, offset }] => {
(value, addr, offset.try_into().map_err(|_| ParseError::LiteralOutOfRange)?)
},
_ => return Err(ParseError::InvalidArguments),

View File

@ -4,7 +4,8 @@
use super::{ModifiesReg, Parsable, ParseError};
use crate::inst::{
basic::{Decodable, Encodable},
parse, InstFmt, ParseCtx, Register,
parse::LineArg,
InstFmt, ParseCtx, Register,
};
use int_conv::{Truncated, ZeroExtended};
use std::convert::TryInto;
@ -48,13 +49,13 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], _ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], _ctx: &Ctx) -> Result<Self, ParseError> {
if mnemonic != "lui" {
return Err(ParseError::UnknownMnemonic);
}
match *args {
[parse::Arg::Register(dst), parse::Arg::Literal(value)] => Ok(Self {
[LineArg::Register(dst), LineArg::Literal(value)] => Ok(Self {
dst,
value: value.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
}),

View File

@ -4,7 +4,8 @@
use super::{ModifiesReg, Parsable, ParseError};
use crate::inst::{
basic::{Decodable, Encodable},
parse, InstFmt, ParseCtx, Register,
parse::LineArg,
InstFmt, ParseCtx, Register,
};
/// Operation kind
@ -155,11 +156,11 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], _ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], _ctx: &Ctx) -> Result<Self, ParseError> {
let inst = match mnemonic {
"mflo" | "mfhi" | "mtlo" | "mthi" => {
let reg = match *args {
[parse::Arg::Register(reg)] => reg,
[LineArg::Register(reg)] => reg,
_ => return Err(ParseError::InvalidArguments),
};
@ -179,7 +180,7 @@ impl Parsable for Inst {
// Mult / Div
"mult" | "multu" | "div" | "divu" => {
let (lhs, rhs) = match *args {
[parse::Arg::Register(lhs), parse::Arg::Register(rhs)] => (lhs, rhs),
[LineArg::Register(lhs), LineArg::Register(rhs)] => (lhs, rhs),
_ => return Err(ParseError::InvalidArguments),
};

View File

@ -8,7 +8,8 @@ pub mod reg;
use super::{ModifiesReg, Parsable, ParseError};
use crate::inst::{
basic::{Decodable, Encodable},
parse, InstFmt, ParseCtx, Register,
parse::LineArg,
InstFmt, ParseCtx, Register,
};
/// Alu register instructions
@ -41,7 +42,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], ctx: &Ctx) -> Result<Self, ParseError> {
match imm::Inst::parse(mnemonic, args, ctx) {
Ok(inst) => Ok(Self::Imm(inst)),
Err(ParseError::UnknownMnemonic) => reg::Inst::parse(mnemonic, args, ctx).map(Self::Reg),

View File

@ -3,10 +3,10 @@
// Imports
use crate::inst::{
basic::{Decodable, Encodable, ModifiesReg, Parsable, ParseError},
parse, InstFmt, ParseCtx, Register,
parse::LineArg,
InstFmt, ParseCtx, Register,
};
use int_conv::{Truncated, ZeroExtended};
use std::convert::TryInto;
use int_conv::{Truncated, ZeroExtended};use std::convert::TryInto;
/// Shift immediate instruction kind
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
@ -96,7 +96,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], _ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], _ctx: &Ctx) -> Result<Self, ParseError> {
let kind = match mnemonic {
"sll" => Kind::LeftLogical,
"srl" => Kind::RightLogical,
@ -105,13 +105,14 @@ impl Parsable for Inst {
};
match *args {
[parse::Arg::Register(lhs @ dst), parse::Arg::Literal(rhs)] |
[parse::Arg::Register(dst), parse::Arg::Register(lhs), parse::Arg::Literal(rhs)] => Ok(Self {
dst,
lhs,
rhs: rhs.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
kind,
}),
[LineArg::Register(lhs @ dst), LineArg::Literal(rhs)] | [LineArg::Register(dst), LineArg::Register(lhs), LineArg::Literal(rhs)] => {
Ok(Self {
dst,
lhs,
rhs: rhs.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
kind,
})
},
_ => Err(ParseError::InvalidArguments),
}
}

View File

@ -3,7 +3,8 @@
// Imports
use crate::inst::{
basic::{Decodable, Encodable, ModifiesReg, Parsable, ParseError},
parse, InstFmt, ParseCtx, Register,
parse::LineArg,
InstFmt, ParseCtx, Register,
};
/// Shift register instruction kind
@ -92,7 +93,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], _ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], _ctx: &Ctx) -> Result<Self, ParseError> {
let kind = match mnemonic {
"sllv" => Kind::LeftLogical,
"srlv" => Kind::RightLogical,
@ -101,8 +102,9 @@ impl Parsable for Inst {
};
match *args {
[parse::Arg::Register(lhs @ dst), parse::Arg::Register(rhs)] |
[parse::Arg::Register(dst), parse::Arg::Register(lhs), parse::Arg::Register(rhs)] => Ok(Self { dst, lhs, rhs, kind }),
[LineArg::Register(lhs @ dst), LineArg::Register(rhs)] | [LineArg::Register(dst), LineArg::Register(lhs), LineArg::Register(rhs)] => {
Ok(Self { dst, lhs, rhs, kind })
},
_ => Err(ParseError::InvalidArguments),
}
}

View File

@ -4,7 +4,8 @@
use super::{ModifiesReg, Parsable, ParseError};
use crate::inst::{
basic::{Decodable, Encodable},
parse, InstFmt, ParseCtx, Register,
parse::LineArg,
InstFmt, ParseCtx, Register,
};
use dcb_util::SignedHex;
use int_conv::{Signed, Truncated, ZeroExtended};
@ -106,7 +107,7 @@ impl Encodable for Inst {
}
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], _ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], _ctx: &Ctx) -> Result<Self, ParseError> {
let kind = match mnemonic {
"sb" => Kind::Byte,
"sh" => Kind::HalfWord,
@ -117,8 +118,8 @@ impl Parsable for Inst {
};
let (value, addr, offset) = match *args {
[parse::Arg::Register(value), parse::Arg::Register(addr)] => (value, addr, 0),
[parse::Arg::Register(value), parse::Arg::RegisterOffset { register: addr, offset }] => {
[LineArg::Register(value), LineArg::Register(addr)] => (value, addr, 0),
[LineArg::Register(value), LineArg::RegisterOffset { register: addr, offset }] => {
(value, addr, offset.try_into().map_err(|_| ParseError::LiteralOutOfRange)?)
},
_ => return Err(ParseError::InvalidArguments),

View File

@ -4,7 +4,8 @@
use super::{ModifiesReg, Parsable, ParseError};
use crate::inst::{
basic::{Decodable, Encodable},
parse, InstFmt, ParseCtx, Register,
parse::LineArg,
InstFmt, ParseCtx, Register,
};
use std::convert::TryInto;
@ -78,7 +79,7 @@ impl Encodable for Inst {
impl Parsable for Inst {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[parse::Arg], _ctx: &Ctx) -> Result<Self, ParseError> {
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &str, args: &[LineArg], _ctx: &Ctx) -> Result<Self, ParseError> {
let kind = match mnemonic {
"sys" => Kind::Sys,
"break" => Kind::Break,
@ -86,7 +87,7 @@ impl Parsable for Inst {
};
let comment = match *args {
[parse::Arg::Literal(comment)] => comment.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
[LineArg::Literal(comment)] => comment.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
_ => return Err(ParseError::InvalidArguments),
};

View File

@ -1,452 +1,7 @@
//! Instruction parsing
// # TODO: Refactor this whole module.
// Modules
pub mod error;
pub mod line;
// Exports
pub use error::ParseError;
// Imports
use crate::inst::Register;
use std::{
borrow::Borrow,
io::{self, Lines},
str::FromStr,
};
/// Instruction parser
#[derive(Debug)]
pub struct InstParser<R: io::BufRead> {
/// Bytes
lines: Lines<R>,
}
impl<R: io::BufRead> InstParser<R> {
/// Creates a new instruction parser
pub fn new(reader: R) -> Self {
let lines = reader.lines();
Self { lines }
}
/// Parses an instruction from a line
// TODO: Avoid allocations where possible
// TODO: Remove everything around this, make it just this function
#[allow(clippy::too_many_lines)] // TODO: Refactor this
#[allow(clippy::shadow_unrelated)] // We can't do this in only one place, fix naming instead
pub fn parse_from_line(line: &str) -> Result<Line, ParseError> {
// Trim the line we read
let line = line.trim();
// If it starts with a comment or it's empty, return an empty line
if line.starts_with('#') || line.is_empty() {
return Ok(Line { label: None, inst: None });
}
// Name character validator
let is_valid_name_char = |c: char| c.is_alphanumeric() || ['.', '_'].contains(&c);
// Literal first character validator
let is_valid_literal_first_char = |c: char| ('0'..='9').contains(&c) || ['+', '-'].contains(&c);
// Read a name
let (name, rest) = match line.find(|c| !is_valid_name_char(c)).map(|idx| line.split_at(idx)) {
// If we got it, remove any whitespace from rest.
Some((name, rest)) => (name, rest.trim_start()),
// If the whole line was a name, return a 0-argument instruction
None => {
return Ok(Line {
label: None,
inst: Some(Inst {
mnemonic: line.to_owned(),
args: vec![],
}),
});
},
};
// If the rest was a comment, or empty, return a 0-argument instruction
if rest.starts_with('#') || rest.is_empty() {
return Ok(Line {
label: None,
inst: Some(Inst {
mnemonic: name.to_owned(),
args: vec![],
}),
});
}
// Check if we have a label
let (label_name, mnemonic, mut args) = match rest.strip_prefix(':').map(str::trim_start) {
// If it started with `:`, read a name after it and return it.
Some(rest) => {
let label = name;
// If it starts with a comment or it's empty, return only the label
if rest.starts_with('#') || rest.is_empty() {
return Ok(Line {
label: Some(Label { name: label.to_owned() }),
inst: None,
});
}
// Ge the mnemonic and arguments
let (mnemonic, args) = match rest.find(|c| !is_valid_name_char(c)).map(|idx| rest.split_at(idx)) {
// If we got it, remove any whitespace from rest.
Some((mnemonic, rest)) => (mnemonic, rest.trim_start()),
// If everything after the label was a name, return a 0-argument label
None => {
return Ok(Line {
label: Some(Label { name: label.to_owned() }),
inst: Some(Inst {
mnemonic: rest.to_owned(),
args: vec![],
}),
});
},
};
(Some(label), mnemonic, args)
},
// Else we have no label
None => (None, name, rest),
};
// Else read arguments
let mut parsed_args = vec![];
loop {
// If the remaining arguments were a comment, or empty, break
if args.starts_with('#') || args.is_empty() {
break;
}
// Else if it starts with a '"', read a string
else if args.starts_with('"') {
// Returns `true` for the first non-escaped '"' in a string
let mut escaping = false;
let find_first_non_escaped_quotes = move |c| match c {
// If we found an escaping character, toggle escape
// Note: If we weren't escaping, it's the start of an escape,
// else it's the '\\' escape.
'\\' => {
escaping ^= true;
false
},
// If we found a '"' while not escaping, finish
'"' if !escaping => true,
// Else set us as not escaping and return
// Note: This is fine even for multi-character escapes, as
// '"' must be escaped with a single character.
_ => {
escaping = false;
false
},
};
// Find the first non-escaped '"'
// Note: The `+2` can never panic.
let string = match args[1..].find(find_first_non_escaped_quotes).map(|idx| args.split_at(idx + 2)) {
Some((string, rest)) => {
args = rest.trim_start();
string
},
None => return Err(ParseError::UnterminatedString),
};
// Create the string and push it
// Note: For whatever reason 'snailquote' requires the quotes to be included in `string`
let string = snailquote::unescape(string).map_err(ParseError::StringUnescape)?;
parsed_args.push(Arg::String(string));
}
// Else if it starts with a number (possibly negative), read a literal
else if args.starts_with(is_valid_literal_first_char) {
// Try to parse a number
let (num, rest) = self::parse_literal(args)?;
args = rest.trim_start();
// If we got a '(' after this literal, read a RegisterOffset instead
if args.starts_with('(') {
// Trim the '(' and whitespace
args = args[1..].trim_start();
// Then make sure it's a register
if !args.starts_with('$') {
return Err(ParseError::ExpectedRegister);
}
// Read it and update the arguments
let reg = args.get(0..=2).ok_or(ParseError::ExpectedRegister)?;
args = args[3..].trim_start();
// If it doesn't end with a ')', return Err
match args.strip_prefix(')') {
Some(rest) => args = rest.trim_start(),
None => return Err(ParseError::UnterminatedRegisterOffset),
}
// Then parse it and push the offset
let register = Register::from_str(reg).map_err(|()| ParseError::UnknownRegister)?;
parsed_args.push(Arg::RegisterOffset { register, offset: num });
}
// Else simply add the literal
else {
parsed_args.push(Arg::Literal(num));
}
}
// Else if it starts with '$', it's a register
else if args.starts_with('$') {
// Try to get the 3 characters forming the register and update the remaining args
let reg = args.get(0..=2).ok_or(ParseError::ExpectedRegister)?;
args = args[3..].trim_start();
// Then parse it and add it
let reg = Register::from_str(reg).map_err(|()| ParseError::UnknownRegister)?;
parsed_args.push(Arg::Register(reg));
}
// Else try to read it as a label with a possible offset
else {
// Read a label name
let (label, offset) = match args.find(|c| !is_valid_name_char(c)).map(|idx| args.split_at(idx)) {
Some((name, rest)) => {
// If the next character is '+'
args = rest.trim_start();
match args.strip_prefix('+') {
Some(rest) => {
// Try to parse a number
let (num, rest) = self::parse_literal(rest)?;
args = rest.trim_start();
// And return the offset
(name, Some(num))
},
None => (name, None),
}
},
// Else the whole rest was just the label
// Note: This can't be empty
None => {
let label = args;
args = "";
(label, None)
},
};
// And add it
let label = match offset {
Some(offset) => Arg::LabelOffset {
label: label.to_owned(),
offset,
},
None => Arg::Label(label.to_owned()),
};
parsed_args.push(label);
}
// If we find a ',', consume and try out the next argument,
// else make sure there are no arguments after this
match args.strip_prefix(',') {
Some(rest) => args = rest.trim_start(),
None => {
// If there's anything remaining, return Err
if !args.starts_with('#') && !args.is_empty() {
return Err(ParseError::ExpectedCommaBetweenArgs);
}
// Else break, we're done
break;
},
}
}
Ok(Line {
label: label_name.map(|name| Label { name: name.to_owned() }),
inst: Some(Inst {
mnemonic: mnemonic.to_owned(),
args: parsed_args,
}),
})
}
}
impl<R: io::BufRead> Iterator for InstParser<R> {
type Item = Result<Line, ParseError>;
fn next(&mut self) -> Option<Self::Item> {
// Get the next line
let line = match self.lines.next()? {
Ok(line) => line,
Err(err) => return Some(Err(ParseError::ReadLine(err))),
};
// Then parse it
Self::parse_from_line(&line).map(Some).transpose()
}
}
/// An instruction line
#[derive(PartialEq, Clone, Debug)]
pub struct Line {
/// Label
pub label: Option<Label>,
/// Instruction
pub inst: Option<Inst>,
}
/// A label
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
pub struct Label {
/// Name
pub name: String,
}
impl Borrow<String> for Label {
fn borrow(&self) -> &String {
&self.name
}
}
/// An instructions
#[derive(PartialEq, Clone, Debug)]
pub struct Inst {
/// Mnemonic
pub mnemonic: String,
/// Components
pub args: Vec<Arg>,
}
/// An argument
#[derive(PartialEq, Clone, Debug)]
pub enum Arg {
/// String
String(String),
/// Register
Register(Register),
/// Register offset
RegisterOffset {
/// The register
register: Register,
/// The offset
offset: i64,
},
/// Literal
Literal(i64),
/// Label
Label(String),
/// LabelOffset
LabelOffset {
/// The label
label: String,
/// The offset
offset: i64,
},
}
impl Arg {
/// Returns this argument as a string
#[must_use]
pub fn as_string(&self) -> Option<&str> {
match self {
Self::String(string) => Some(string),
_ => None,
}
}
/// Returns this argument as a register
#[must_use]
pub const fn as_register(&self) -> Option<Register> {
match *self {
Self::Register(reg) => Some(reg),
_ => None,
}
}
/// Returns this argument as a register offset
#[must_use]
pub const fn as_register_offset(&self) -> Option<(Register, i64)> {
match *self {
Self::RegisterOffset { register, offset } => Some((register, offset)),
_ => None,
}
}
/// Returns this argument as a literal
#[must_use]
pub const fn as_literal(&self) -> Option<i64> {
match *self {
Self::Literal(literal) => Some(literal),
_ => None,
}
}
/// Returns this argument as a label
#[must_use]
pub fn as_label(&self) -> Option<&str> {
match self {
Self::Label(label) => Some(label),
_ => None,
}
}
/// Returns this argument as a label offset
#[must_use]
pub fn as_label_offset(&self) -> Option<(&str, i64)> {
match self {
Self::LabelOffset { label, offset } => Some((label, *offset)),
_ => None,
}
}
}
/// Parses a literal from a string and returns the rest
pub fn parse_literal(s: &str) -> Result<(i64, &str), ParseError> {
// Check if it's negative
let (is_neg, num) = match s.chars().next() {
Some('+') => (false, &s[1..]),
Some('-') => (true, &s[1..]),
_ => (false, s),
};
// Check if we have a base
let (base, num) = match num.as_bytes() {
[b'0', b'x', ..] => (16, &num[2..]),
[b'0', b'o', ..] => (8, &num[2..]),
[b'0', b'b', ..] => (2, &num[2..]),
_ => (10, num),
};
// Returns if 'c' is a valid digit for the current base
let is_valid_digit = |c| match base {
16 => ('0'..='9').contains(&c) || ('a'..='f').contains(&c),
10 => ('0'..='9').contains(&c),
8 => ('0'..='8').contains(&c),
2 => ('0'..='1').contains(&c),
_ => todo!("Unsupported base"),
};
// Then check where the number ends
let (num, rest) = match num.find(|c| !is_valid_digit(c)).map(|idx| num.split_at(idx)) {
Some((num, rest)) => (num, rest),
None => (num, ""),
};
// Parse it
let num = i64::from_str_radix(num, base).map_err(ParseError::ParseLiteral)?;
let num = match is_neg {
true => -num,
false => num,
};
Ok((num, rest))
}
pub use line::{Line, LineArg, LineInst, LineLabel};

View File

@ -0,0 +1,452 @@
//! Line parsing
// # TODO: Refactor this whole module.
// Modules
pub mod error;
// Exports
pub use error::ParseError;
// Imports
use crate::inst::Register;
use std::{
borrow::Borrow,
io::{self, Lines},
str::FromStr,
};
/// Instruction parser
#[derive(Debug)]
pub struct InstParser<R: io::BufRead> {
/// Bytes
lines: Lines<R>,
}
impl<R: io::BufRead> InstParser<R> {
/// Creates a new instruction parser
pub fn new(reader: R) -> Self {
let lines = reader.lines();
Self { lines }
}
/// Parses an instruction from a line
// TODO: Avoid allocations where possible
// TODO: Remove everything around this, make it just this function
#[allow(clippy::too_many_lines)] // TODO: Refactor this
#[allow(clippy::shadow_unrelated)] // We can't do this in only one place, fix naming instead
pub fn parse_from_line(line: &str) -> Result<Line, ParseError> {
// Trim the line we read
let line = line.trim();
// If it starts with a comment or it's empty, return an empty line
if line.starts_with('#') || line.is_empty() {
return Ok(Line { label: None, inst: None });
}
// Name character validator
let is_valid_name_char = |c: char| c.is_alphanumeric() || ['.', '_'].contains(&c);
// Literal first character validator
let is_valid_literal_first_char = |c: char| ('0'..='9').contains(&c) || ['+', '-'].contains(&c);
// Read a name
let (name, rest) = match line.find(|c| !is_valid_name_char(c)).map(|idx| line.split_at(idx)) {
// If we got it, remove any whitespace from rest.
Some((name, rest)) => (name, rest.trim_start()),
// If the whole line was a name, return a 0-argument instruction
None => {
return Ok(Line {
label: None,
inst: Some(LineInst {
mnemonic: line.to_owned(),
args: vec![],
}),
});
},
};
// If the rest was a comment, or empty, return a 0-argument instruction
if rest.starts_with('#') || rest.is_empty() {
return Ok(Line {
label: None,
inst: Some(LineInst {
mnemonic: name.to_owned(),
args: vec![],
}),
});
}
// Check if we have a label
let (label_name, mnemonic, mut args) = match rest.strip_prefix(':').map(str::trim_start) {
// If it started with `:`, read a name after it and return it.
Some(rest) => {
let label = name;
// If it starts with a comment or it's empty, return only the label
if rest.starts_with('#') || rest.is_empty() {
return Ok(Line {
label: Some(LineLabel { name: label.to_owned() }),
inst: None,
});
}
// Ge the mnemonic and arguments
let (mnemonic, args) = match rest.find(|c| !is_valid_name_char(c)).map(|idx| rest.split_at(idx)) {
// If we got it, remove any whitespace from rest.
Some((mnemonic, rest)) => (mnemonic, rest.trim_start()),
// If everything after the label was a name, return a 0-argument label
None => {
return Ok(Line {
label: Some(LineLabel { name: label.to_owned() }),
inst: Some(LineInst {
mnemonic: rest.to_owned(),
args: vec![],
}),
});
},
};
(Some(label), mnemonic, args)
},
// Else we have no label
None => (None, name, rest),
};
// Else read arguments
let mut parsed_args = vec![];
loop {
// If the remaining arguments were a comment, or empty, break
if args.starts_with('#') || args.is_empty() {
break;
}
// Else if it starts with a '"', read a string
else if args.starts_with('"') {
// Returns `true` for the first non-escaped '"' in a string
let mut escaping = false;
let find_first_non_escaped_quotes = move |c| match c {
// If we found an escaping character, toggle escape
// Note: If we weren't escaping, it's the start of an escape,
// else it's the '\\' escape.
'\\' => {
escaping ^= true;
false
},
// If we found a '"' while not escaping, finish
'"' if !escaping => true,
// Else set us as not escaping and return
// Note: This is fine even for multi-character escapes, as
// '"' must be escaped with a single character.
_ => {
escaping = false;
false
},
};
// Find the first non-escaped '"'
// Note: The `+2` can never panic.
let string = match args[1..].find(find_first_non_escaped_quotes).map(|idx| args.split_at(idx + 2)) {
Some((string, rest)) => {
args = rest.trim_start();
string
},
None => return Err(ParseError::UnterminatedString),
};
// Create the string and push it
// Note: For whatever reason 'snailquote' requires the quotes to be included in `string`
let string = snailquote::unescape(string).map_err(ParseError::StringUnescape)?;
parsed_args.push(LineArg::String(string));
}
// Else if it starts with a number (possibly negative), read a literal
else if args.starts_with(is_valid_literal_first_char) {
// Try to parse a number
let (num, rest) = self::parse_literal(args)?;
args = rest.trim_start();
// If we got a '(' after this literal, read a RegisterOffset instead
if args.starts_with('(') {
// Trim the '(' and whitespace
args = args[1..].trim_start();
// Then make sure it's a register
if !args.starts_with('$') {
return Err(ParseError::ExpectedRegister);
}
// Read it and update the arguments
let reg = args.get(0..=2).ok_or(ParseError::ExpectedRegister)?;
args = args[3..].trim_start();
// If it doesn't end with a ')', return Err
match args.strip_prefix(')') {
Some(rest) => args = rest.trim_start(),
None => return Err(ParseError::UnterminatedRegisterOffset),
}
// Then parse it and push the offset
let register = Register::from_str(reg).map_err(|()| ParseError::UnknownRegister)?;
parsed_args.push(LineArg::RegisterOffset { register, offset: num });
}
// Else simply add the literal
else {
parsed_args.push(LineArg::Literal(num));
}
}
// Else if it starts with '$', it's a register
else if args.starts_with('$') {
// Try to get the 3 characters forming the register and update the remaining args
let reg = args.get(0..=2).ok_or(ParseError::ExpectedRegister)?;
args = args[3..].trim_start();
// Then parse it and add it
let reg = Register::from_str(reg).map_err(|()| ParseError::UnknownRegister)?;
parsed_args.push(LineArg::Register(reg));
}
// Else try to read it as a label with a possible offset
else {
// Read a label name
let (label, offset) = match args.find(|c| !is_valid_name_char(c)).map(|idx| args.split_at(idx)) {
Some((name, rest)) => {
// If the next character is '+'
args = rest.trim_start();
match args.strip_prefix('+') {
Some(rest) => {
// Try to parse a number
let (num, rest) = self::parse_literal(rest)?;
args = rest.trim_start();
// And return the offset
(name, Some(num))
},
None => (name, None),
}
},
// Else the whole rest was just the label
// Note: This can't be empty
None => {
let label = args;
args = "";
(label, None)
},
};
// And add it
let label = match offset {
Some(offset) => LineArg::LabelOffset {
label: label.to_owned(),
offset,
},
None => LineArg::Label(label.to_owned()),
};
parsed_args.push(label);
}
// If we find a ',', consume and try out the next argument,
// else make sure there are no arguments after this
match args.strip_prefix(',') {
Some(rest) => args = rest.trim_start(),
None => {
// If there's anything remaining, return Err
if !args.starts_with('#') && !args.is_empty() {
return Err(ParseError::ExpectedCommaBetweenArgs);
}
// Else break, we're done
break;
},
}
}
Ok(Line {
label: label_name.map(|name| LineLabel { name: name.to_owned() }),
inst: Some(LineInst {
mnemonic: mnemonic.to_owned(),
args: parsed_args,
}),
})
}
}
impl<R: io::BufRead> Iterator for InstParser<R> {
type Item = Result<Line, ParseError>;
fn next(&mut self) -> Option<Self::Item> {
// Get the next line
let line = match self.lines.next()? {
Ok(line) => line,
Err(err) => return Some(Err(ParseError::ReadLine(err))),
};
// Then parse it
Self::parse_from_line(&line).map(Some).transpose()
}
}
/// An instruction line
#[derive(PartialEq, Clone, Debug)]
pub struct Line {
/// Label
pub label: Option<LineLabel>,
/// Instruction
pub inst: Option<LineInst>,
}
/// A label
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
pub struct LineLabel {
/// Name
pub name: String,
}
impl Borrow<String> for LineLabel {
fn borrow(&self) -> &String {
&self.name
}
}
/// An instructions
#[derive(PartialEq, Clone, Debug)]
pub struct LineInst {
/// Mnemonic
pub mnemonic: String,
/// Components
pub args: Vec<LineArg>,
}
/// An argument
#[derive(PartialEq, Clone, Debug)]
pub enum LineArg {
/// String
String(String),
/// Register
Register(Register),
/// Register offset
RegisterOffset {
/// The register
register: Register,
/// The offset
offset: i64,
},
/// Literal
Literal(i64),
/// Label
Label(String),
/// LabelOffset
LabelOffset {
/// The label
label: String,
/// The offset
offset: i64,
},
}
impl LineArg {
/// Returns this argument as a string
#[must_use]
pub fn as_string(&self) -> Option<&str> {
match self {
Self::String(string) => Some(string),
_ => None,
}
}
/// Returns this argument as a register
#[must_use]
pub const fn as_register(&self) -> Option<Register> {
match *self {
Self::Register(reg) => Some(reg),
_ => None,
}
}
/// Returns this argument as a register offset
#[must_use]
pub const fn as_register_offset(&self) -> Option<(Register, i64)> {
match *self {
Self::RegisterOffset { register, offset } => Some((register, offset)),
_ => None,
}
}
/// Returns this argument as a literal
#[must_use]
pub const fn as_literal(&self) -> Option<i64> {
match *self {
Self::Literal(literal) => Some(literal),
_ => None,
}
}
/// Returns this argument as a label
#[must_use]
pub fn as_label(&self) -> Option<&str> {
match self {
Self::Label(label) => Some(label),
_ => None,
}
}
/// Returns this argument as a label offset
#[must_use]
pub fn as_label_offset(&self) -> Option<(&str, i64)> {
match self {
Self::LabelOffset { label, offset } => Some((label, *offset)),
_ => None,
}
}
}
/// Parses a literal from a string and returns the rest
fn parse_literal(s: &str) -> Result<(i64, &str), ParseError> {
// Check if it's negative
let (is_neg, num) = match s.chars().next() {
Some('+') => (false, &s[1..]),
Some('-') => (true, &s[1..]),
_ => (false, s),
};
// Check if we have a base
let (base, num) = match num.as_bytes() {
[b'0', b'x', ..] => (16, &num[2..]),
[b'0', b'o', ..] => (8, &num[2..]),
[b'0', b'b', ..] => (2, &num[2..]),
_ => (10, num),
};
// Returns if 'c' is a valid digit for the current base
let is_valid_digit = |c| match base {
16 => ('0'..='9').contains(&c) || ('a'..='f').contains(&c),
10 => ('0'..='9').contains(&c),
8 => ('0'..='8').contains(&c),
2 => ('0'..='1').contains(&c),
_ => todo!("Unsupported base"),
};
// Then check where the number ends
let (num, rest) = match num.find(|c| !is_valid_digit(c)).map(|idx| num.split_at(idx)) {
Some((num, rest)) => (num, rest),
None => (num, ""),
};
// Parse it
let num = i64::from_str_radix(num, base).map_err(ParseError::ParseLiteral)?;
let num = match is_neg {
true => -num,
false => num,
};
Ok((num, rest))
}