mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-04 08:23:13 +00:00
Moved line parsing into it's own module and renamed it's types to be preprended with Line.
This commit is contained in:
parent
17dfe03800
commit
88cb7ed679
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)?,
|
||||
|
||||
@ -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),
|
||||
},
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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),
|
||||
},
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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)?,
|
||||
}),
|
||||
|
||||
@ -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),
|
||||
};
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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),
|
||||
};
|
||||
|
||||
|
||||
@ -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};
|
||||
|
||||
452
dcb-exe/src/inst/parse/line.rs
Normal file
452
dcb-exe/src/inst/parse/line.rs
Normal 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))
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user