Cleanup useless errors, add help messages for Runtime Errors, tag registers with which register they should be filling
This commit is contained in:
parent
f0b5be8c63
commit
2122535f56
6 changed files with 223 additions and 145 deletions
72
src/env.rs
72
src/env.rs
|
@ -1,6 +1,4 @@
|
|||
use std::{collections::HashMap, env::Args};
|
||||
|
||||
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
err::RuntimeErr,
|
||||
|
@ -16,12 +14,6 @@ pub enum SymbolValue {
|
|||
String(String),
|
||||
}
|
||||
|
||||
pub enum Value {
|
||||
Immediate(i64),
|
||||
Register(u32),
|
||||
Symbol(String, SymbolValue),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Env {
|
||||
pub register_alias: HashMap<String, u32>,
|
||||
|
@ -77,7 +69,7 @@ impl Env {
|
|||
register_alias,
|
||||
labels: HashMap::new(),
|
||||
registers: [0; 32],
|
||||
stack: Vec::from([0; 1024]),
|
||||
stack: Vec::from([0; 1024]), // 1024 * 64 = 64 KiB stack
|
||||
instructions: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -126,20 +118,25 @@ impl Env {
|
|||
self.labels.get(label).copied()
|
||||
}
|
||||
|
||||
pub fn assemble_op(&mut self, op: (Token, Loc)) -> Result<u32, RuntimeErr> {
|
||||
pub fn assemble_op(
|
||||
&self,
|
||||
op: (Token, Loc),
|
||||
) -> Result<u32, (RuntimeErr, Loc, Option<String>)> {
|
||||
if let (Token::Op(name, args), loc) = op {
|
||||
let i = instruction(&name);
|
||||
let mut imm = 0u32;
|
||||
let mut regs = vec![0; 4];
|
||||
if args.len() != i.1.len() {
|
||||
return Err(RuntimeErr::InvalidOpArity(
|
||||
name,
|
||||
args.len(),
|
||||
i.1.len(),
|
||||
return Err((
|
||||
RuntimeErr::InvalidOpArity(name, args.len(), i.1.len()),
|
||||
loc,
|
||||
None,
|
||||
));
|
||||
}
|
||||
|
||||
let _ = i.1.into_par_iter()
|
||||
let _ =
|
||||
i.1.clone()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.try_for_each(|(k, v)| match v {
|
||||
Arg::Immediate => {
|
||||
|
@ -147,28 +144,53 @@ impl Env {
|
|||
imm = i as u32;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RuntimeErr::InvalidType("Immediate".to_string(), v.kind()))
|
||||
Err((
|
||||
RuntimeErr::InvalidType("immediate".to_string(), v.kind()),
|
||||
args[k].1,
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
Arg::Register => {
|
||||
if let Token::Register(r) = args[k].0 {
|
||||
regs[k] = self.reg_to_register(&r).unwrap();
|
||||
Arg::Register(id) => {
|
||||
if let Token::Register(r) = &args[k].0 {
|
||||
regs[id] = self.reg_to_register(&r).unwrap();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RuntimeErr::InvalidType("Register".to_string(), v.kind()))
|
||||
Err((
|
||||
RuntimeErr::InvalidType("register".to_string(), v.kind()),
|
||||
args[k].1,
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
Arg::Memory => {
|
||||
if let Token::Memory(i, r) = args[k].0 {
|
||||
if let Token::Memory(i, r) = &args[k].0 {
|
||||
if r.is_some() {
|
||||
regs[k] = self.reg_to_register(&r.unwrap()).unwrap();
|
||||
regs[k] = self
|
||||
.reg_to_register(&if let Token::Register(r) =
|
||||
*(r.clone().unwrap())
|
||||
{
|
||||
r
|
||||
} else {
|
||||
unreachable!()
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
imm = if let Token::Immediate(i) = **i {
|
||||
i as u32
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RuntimeErr::InvalidType("Memory".to_string(), v.kind()))
|
||||
Err((
|
||||
RuntimeErr::InvalidType("memory".to_string(), v.kind()),
|
||||
args[k].1,
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => unimplemented!()
|
||||
_ => unimplemented!(),
|
||||
})?;
|
||||
Ok(u32::from_str_radix(&with(i, imm, regs).0.to_string(), 2).unwrap())
|
||||
} else {
|
||||
|
|
72
src/err.rs
72
src/err.rs
|
@ -1,8 +1,14 @@
|
|||
use std::fmt::{self, Display, Formatter};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
fmt::{self, Display, Formatter},
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::instructions::instruction;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SyntaxErr {
|
||||
TraillingComma,
|
||||
/// false for '(' true for ')'
|
||||
UnmatchedParen(bool),
|
||||
UnexpectedChar,
|
||||
|
@ -13,10 +19,9 @@ pub enum SyntaxErr {
|
|||
impl Display for SyntaxErr {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
SyntaxErr::TraillingComma => write!(f, "trailling comma"),
|
||||
SyntaxErr::UnmatchedParen(_) => write!(f, "unmatched parenthesis"),
|
||||
SyntaxErr::UnexpectedChar => write!(f, "unexpected character"),
|
||||
SyntaxErr::OutsideOp(kind) => write!(f, "{kind} before opcode"),
|
||||
SyntaxErr::OutsideOp(kind) => write!(f, "`{kind}` before opcode"),
|
||||
SyntaxErr::MemoryInvalidRegister => write!(f, "invalid register"),
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +30,10 @@ impl Display for SyntaxErr {
|
|||
impl SyntaxErr {
|
||||
pub fn note(&self) -> String {
|
||||
match self {
|
||||
SyntaxErr::TraillingComma => "remove the final comma".to_string(),
|
||||
SyntaxErr::UnmatchedParen(false) => "add ')' after the register name".to_string(),
|
||||
SyntaxErr::UnmatchedParen(true) => "add '(' before the register name".to_string(),
|
||||
SyntaxErr::UnmatchedParen(false) => "add `)` after the register name".to_string(),
|
||||
SyntaxErr::UnmatchedParen(true) => "add `(` before the register name".to_string(),
|
||||
SyntaxErr::UnexpectedChar => "ensure the input is well-formed".to_string(),
|
||||
SyntaxErr::OutsideOp(_) => format!("add arguments after the opcode"),
|
||||
SyntaxErr::OutsideOp(_) => format!("only add arguments after the opcode"),
|
||||
SyntaxErr::MemoryInvalidRegister => {
|
||||
"registers are either xN (N < 32 with no leading 0) or the standard aliases"
|
||||
.to_string()
|
||||
|
@ -40,29 +44,53 @@ impl SyntaxErr {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RuntimeErr {
|
||||
InvalidRegister(String),
|
||||
UnexpectedImmediate,
|
||||
UnexpectedRegister,
|
||||
InvalidOp(String),
|
||||
InvalidOp,
|
||||
/// op, actual, expected
|
||||
InvalidOpArity(String, usize, usize),
|
||||
/// actual, expected
|
||||
InvalidType(String, String),
|
||||
}
|
||||
|
||||
impl Display for RuntimeErr {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
RuntimeErr::InvalidRegister(reg) => write!(f, "invalid register {}", reg),
|
||||
RuntimeErr::UnexpectedImmediate => write!(f, "unexpected immediate"),
|
||||
RuntimeErr::UnexpectedRegister => write!(f, "unexpected register"),
|
||||
RuntimeErr::InvalidOp(op) => write!(f, "invalid operation {}", op),
|
||||
RuntimeErr::InvalidOpArity(op, actual, expected) => write!(
|
||||
f,
|
||||
"invalid operation arity {} expected {} got {}",
|
||||
op, expected, actual
|
||||
),
|
||||
RuntimeErr::InvalidOp => write!(f, "invalid operation"),
|
||||
RuntimeErr::InvalidOpArity(op, actual, expected) => {
|
||||
write!(f, "`{}` expected {} args, got {}", op, expected, actual)
|
||||
}
|
||||
RuntimeErr::InvalidType(actual, expected) => {
|
||||
write!(f, "expected {}, got {}", expected, actual)
|
||||
write!(f, "expected `{}`, got `{}`", expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeErr {
|
||||
pub fn note(&self) -> String {
|
||||
match self {
|
||||
RuntimeErr::InvalidOp => "check the ref sheet for the avaliable opcodes".to_string(),
|
||||
RuntimeErr::InvalidOpArity(op, actual, expected) => {
|
||||
let args = instruction(op).1;
|
||||
match actual.cmp(expected) {
|
||||
Ordering::Equal => unreachable!(),
|
||||
Ordering::Greater if actual - expected == 1 => {
|
||||
"remove the extra argument".to_string()
|
||||
}
|
||||
Ordering::Greater => "remove the extra arguments".to_string(),
|
||||
Ordering::Less if expected - actual == 1 => {
|
||||
format!("add the extra `{}` argument", args.last().unwrap().kind())
|
||||
}
|
||||
Ordering::Less => format!(
|
||||
"add the extra `{}` arguments",
|
||||
args.get((actual - 1)..)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|arg| arg.kind())
|
||||
.join("`, `")
|
||||
),
|
||||
}
|
||||
}
|
||||
RuntimeErr::InvalidType(_, _) => "ensure the operation is valid".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -222,9 +222,12 @@ pub mod kind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Arg {
|
||||
Register,
|
||||
/// rd -> 0, ra -> 1, rb -> 2, rc -> 3
|
||||
Register(usize),
|
||||
Immediate,
|
||||
/// always ra
|
||||
Memory,
|
||||
Symbol,
|
||||
}
|
||||
|
@ -232,7 +235,7 @@ pub enum Arg {
|
|||
impl Arg {
|
||||
pub fn kind(&self) -> String {
|
||||
match self {
|
||||
Arg::Register => "register",
|
||||
Arg::Register(_) => "register",
|
||||
Arg::Immediate => "immediate",
|
||||
Arg::Memory => "memory",
|
||||
Arg::Symbol => "symbol",
|
||||
|
@ -250,14 +253,14 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
|
|||
"nop" => (Kind::Pseudo(Pseudo {}), vec![]),
|
||||
|
||||
// Move
|
||||
"li" => (Kind::Pseudo(Pseudo {}), vec![Arg::Register, Arg::Immediate]),
|
||||
"li" => (Kind::Pseudo(Pseudo {}), vec![Arg::Register(0), Arg::Immediate]),
|
||||
"lui" => (
|
||||
Kind::U(U {
|
||||
imm: to_bits(0),
|
||||
rd: to_bits(0),
|
||||
opcode: to_bits(0b0110111),
|
||||
}),
|
||||
vec![Arg::Register, Arg::Immediate],
|
||||
vec![Arg::Register(0), Arg::Immediate],
|
||||
),
|
||||
|
||||
// Memory
|
||||
|
@ -270,7 +273,7 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
|
|||
imm2: to_bits(0),
|
||||
opcode: to_bits(0b0100011),
|
||||
}),
|
||||
vec![Arg::Register, Arg::Memory],
|
||||
vec![Arg::Register(2), Arg::Memory],
|
||||
),
|
||||
|
||||
// Arithmetic, Logic, Shift
|
||||
|
@ -283,7 +286,7 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
|
|||
rd: to_bits(0),
|
||||
opcode: to_bits(0b0110011),
|
||||
}),
|
||||
vec![Arg::Register, Arg::Register, Arg::Register],
|
||||
vec![Arg::Register(0), Arg::Register(1), Arg::Register(2)],
|
||||
),
|
||||
"addi" => (
|
||||
Kind::I(I {
|
||||
|
@ -293,7 +296,7 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
|
|||
rd: to_bits(0),
|
||||
opcode: to_bits(0b0010011),
|
||||
}),
|
||||
vec![Arg::Register, Arg::Register, Arg::Immediate],
|
||||
vec![Arg::Register(0), Arg::Register(1), Arg::Immediate],
|
||||
),
|
||||
// Multiply, Divide
|
||||
|
||||
|
@ -309,7 +312,7 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
|
|||
imm2: to_bits(0),
|
||||
opcode: to_bits(0b1100011),
|
||||
}),
|
||||
vec![Arg::Register, Arg::Register, Arg::Immediate],
|
||||
vec![Arg::Register(1), Arg::Register(2), Arg::Immediate],
|
||||
),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
|
49
src/main.rs
49
src/main.rs
|
@ -9,8 +9,7 @@ use codespan_reporting::{
|
|||
};
|
||||
use riscv_interpreter::{
|
||||
env::Env,
|
||||
instructions::{instruction, with},
|
||||
parser::parse,
|
||||
parser::{parse, Token},
|
||||
};
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
|
@ -24,10 +23,45 @@ fn main() -> anyhow::Result<()> {
|
|||
|
||||
match parse(&env, &input) {
|
||||
Ok(tokens) => {
|
||||
println!("{:#?} -> {:#?}", input, tokens);
|
||||
let lines: Vec<&str> = input.lines().collect();
|
||||
let size = lines.iter().map(|l| l.len()).max().unwrap();
|
||||
|
||||
tokens.iter().enumerate().for_each(|(line, token)| {
|
||||
let token = token.clone();
|
||||
let mut formatted = format!("{:<1$}", lines[line].to_string() + ":", size + 3);
|
||||
|
||||
match token.0 {
|
||||
Token::Op(..) => match env.assemble_op(token) {
|
||||
Ok(op) => {
|
||||
formatted += &format!("{:032b}", op);
|
||||
println!("{}", formatted);
|
||||
}
|
||||
Err(err) => {
|
||||
let diagnostic = Diagnostic::error()
|
||||
.with_message("Runtime Error")
|
||||
.with_labels(vec![Label::primary((), err.1.start..err.1.end)
|
||||
.with_message(err.0.to_string())])
|
||||
.with_notes({
|
||||
let mut notes = Vec::new();
|
||||
if let Some(note) = &err.2 {
|
||||
notes.push(note.to_string());
|
||||
}
|
||||
notes.push(err.0.note());
|
||||
notes
|
||||
});
|
||||
|
||||
term::emit(&mut writer.lock(), &config, &file, &diagnostic).unwrap();
|
||||
}
|
||||
},
|
||||
Token::LabelDef(name) => {
|
||||
println!("{name}:");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(errs) => {
|
||||
for err in errs {
|
||||
let err = errs.first().unwrap();
|
||||
let start = err.1.start;
|
||||
let end = err.1.end + 1;
|
||||
|
||||
|
@ -38,19 +72,18 @@ fn main() -> anyhow::Result<()> {
|
|||
])
|
||||
.with_notes({
|
||||
let mut notes = Vec::new();
|
||||
if let Some(note) = err.3 {
|
||||
notes.push(note);
|
||||
if let Some(note) = &err.3 {
|
||||
notes.push(note.to_string());
|
||||
}
|
||||
notes.push(err.0.note());
|
||||
notes
|
||||
});
|
||||
|
||||
term::emit(&mut writer.lock(), &config, &file, &diagnostic).unwrap();
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -57,18 +57,14 @@ pub struct Loc {
|
|||
pub end: usize,
|
||||
}
|
||||
|
||||
fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
|
||||
let mut loc = Loc {
|
||||
line,
|
||||
start: 0,
|
||||
end: 0,
|
||||
};
|
||||
|
||||
fn parse_line(env: &Env, input: &str, line: usize, loc: &mut Loc) -> Result<Vec<(Token, Loc)>, ParseErr> {
|
||||
let mut tokens: Vec<(Token, Loc)> = Vec::new();
|
||||
let mut chars = input.chars().peekable();
|
||||
|
||||
use Token::*;
|
||||
|
||||
loc.line = line;
|
||||
|
||||
while let Some(c) = chars.next() {
|
||||
let token = match c {
|
||||
'\t' | ' ' => Spacing,
|
||||
|
@ -87,7 +83,7 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
|||
num.push(chars.next().unwrap());
|
||||
loc.end += 1;
|
||||
}
|
||||
if let Some('(') | Some(' ') = chars.peek() {
|
||||
if let Some('(') | Some(' ') | None = chars.peek() {
|
||||
Immediate(num.parse().unwrap())
|
||||
} else {
|
||||
return Err((
|
||||
|
@ -170,8 +166,8 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
|||
// Opcode or Label definition
|
||||
'a'..='z' | 'A'..='Z' | '_' => {
|
||||
let mut str = c.to_string();
|
||||
while let Some('a'..='z') | Some('A'..='Z') | Some('_') | Some('0'..='9') | Some('.') =
|
||||
chars.peek()
|
||||
while let Some('a'..='z') | Some('A'..='Z') | Some('_') | Some('0'..='9')
|
||||
| Some('.') = chars.peek()
|
||||
{
|
||||
str.push(chars.next().unwrap());
|
||||
loc.end += 1;
|
||||
|
@ -180,34 +176,16 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
|||
chars.next();
|
||||
loc.end += 1;
|
||||
LabelDef(str[..str.len()].to_string())
|
||||
} else if let Some((Op(_, _), _)) = tokens.get(tokens.len() - 2) {
|
||||
// These Registers may actually be label references or symbols, but there's ambiguity
|
||||
} else {
|
||||
// These Registers may actually be ops, label references or symbols, but there's ambiguity
|
||||
// between them and registers, so we'll just assume they're registers for now
|
||||
Register(str.trim().to_owned())
|
||||
} else {
|
||||
if env.is_valid_register(&str) {
|
||||
return Err((
|
||||
SyntaxErr::OutsideOp("register".to_string()),
|
||||
loc.clone(),
|
||||
tokens.clone(),
|
||||
None,
|
||||
));
|
||||
}
|
||||
if str.trim().contains(|c: char| !c.is_alphabetic() && c != '.') {
|
||||
return Err((
|
||||
SyntaxErr::UnexpectedChar,
|
||||
dbg!(loc.clone()),
|
||||
tokens.clone(),
|
||||
Some("opcodes must only contain ascii letters".to_string()),
|
||||
));
|
||||
}
|
||||
Op(str, vec![])
|
||||
}
|
||||
}
|
||||
_ => return Err((SyntaxErr::UnexpectedChar, loc.clone(), tokens.clone(), None)),
|
||||
};
|
||||
tokens.push((token, loc.clone()));
|
||||
loc.end += 1;
|
||||
loc.end += 1; // Newline
|
||||
loc.start = loc.end;
|
||||
}
|
||||
|
||||
|
@ -215,10 +193,7 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
|||
.into_iter()
|
||||
.filter(|(token, _)| !matches!(token, Token::Spacing))
|
||||
.group_by(|(token, _)| {
|
||||
matches!(
|
||||
token,
|
||||
Op(_, _) | Immediate(_) | Register(_) | Memory(_, _) | Symbol(_) | String(_)
|
||||
)
|
||||
matches!(token, Immediate(_) | Register(_) | Memory(_, _) | Symbol(_))
|
||||
})
|
||||
.into_iter()
|
||||
.flat_map(|group| {
|
||||
|
@ -226,8 +201,8 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
|||
if is_op {
|
||||
let group = group.collect::<Vec<_>>();
|
||||
let (op, loc) = group[0].clone();
|
||||
let (op, mut args) = match op {
|
||||
Op(op, args) => (op, args),
|
||||
let (name, mut args) = match op {
|
||||
Register(r) => (r, vec![]),
|
||||
// because any register/symbol/label def is interpreted as an Op by default, this only
|
||||
// partially works. This does trigger on immediate values and memory indexes
|
||||
_ => {
|
||||
|
@ -242,9 +217,21 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
|||
)]
|
||||
}
|
||||
};
|
||||
if env.is_valid_register(&name) {
|
||||
return vec![(
|
||||
Token::Error((
|
||||
SyntaxErr::OutsideOp("register".to_string()),
|
||||
loc.clone(),
|
||||
group.clone(),
|
||||
None,
|
||||
)),
|
||||
loc.clone(),
|
||||
)];
|
||||
}
|
||||
|
||||
args.extend_from_slice(&group[1..]);
|
||||
|
||||
vec![(Op(op, args), loc)]
|
||||
vec![(Op(name, args), loc)]
|
||||
} else {
|
||||
group.collect::<Vec<_>>()
|
||||
}
|
||||
|
@ -265,21 +252,26 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
|||
/// containing the error, the location of the error, the tokens parsed up to that point,
|
||||
/// and an optional message to display to the users for each line with an error
|
||||
pub fn parse(env: &Env, input: &str) -> Result<Vec<(Token, Loc)>, Vec<ParseErr>> {
|
||||
let mut loc = Loc {
|
||||
line: 0,
|
||||
start: 0,
|
||||
end: 0,
|
||||
};
|
||||
|
||||
let parsed_lines = input
|
||||
.lines()
|
||||
.enumerate()
|
||||
.par_bridge()
|
||||
.map(|(i, line)| parse_line(env, line, i + 1))
|
||||
.map(|(i, line)| parse_line(env, line, i + 1, &mut loc))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let (ok, err) = parsed_lines
|
||||
.into_par_iter()
|
||||
.partition::<Vec<Result<_, _>>, Vec<Result<_, _>>, _>(|line| matches!(line, Ok(_)));
|
||||
dbg!(&err);
|
||||
let (ok, err): (Vec<_>, Vec<_>) = parsed_lines.into_iter()
|
||||
.partition(|line| matches!(line, Ok(_)));
|
||||
|
||||
dbg!(err.clone());
|
||||
|
||||
if err.is_empty() {
|
||||
Ok(ok.into_par_iter().flat_map(|line| line.unwrap()).collect())
|
||||
Ok(ok.into_iter().flat_map(|line| line.unwrap()).collect())
|
||||
} else {
|
||||
Err(err.into_par_iter().map(|line| line.unwrap_err()).collect())
|
||||
Err(err.into_iter().map(|line| line.unwrap_err()).collect())
|
||||
}
|
||||
}
|
||||
|
|
4
test.s
4
test.s
|
@ -1,4 +1,4 @@
|
|||
a:
|
||||
addi a0 zero 1
|
||||
a0 addi a0 zero 1
|
||||
addi a1 zero 2
|
||||
add a2 a1 a0
|
||||
a0 add a2 a1 a0
|
||||
|
|
Loading…
Reference in a new issue