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
110
src/env.rs
110
src/env.rs
|
@ -1,6 +1,4 @@
|
||||||
use std::{collections::HashMap, env::Args};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
err::RuntimeErr,
|
err::RuntimeErr,
|
||||||
|
@ -16,12 +14,6 @@ pub enum SymbolValue {
|
||||||
String(String),
|
String(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Value {
|
|
||||||
Immediate(i64),
|
|
||||||
Register(u32),
|
|
||||||
Symbol(String, SymbolValue),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Env {
|
pub struct Env {
|
||||||
pub register_alias: HashMap<String, u32>,
|
pub register_alias: HashMap<String, u32>,
|
||||||
|
@ -77,7 +69,7 @@ impl Env {
|
||||||
register_alias,
|
register_alias,
|
||||||
labels: HashMap::new(),
|
labels: HashMap::new(),
|
||||||
registers: [0; 32],
|
registers: [0; 32],
|
||||||
stack: Vec::from([0; 1024]),
|
stack: Vec::from([0; 1024]), // 1024 * 64 = 64 KiB stack
|
||||||
instructions: Vec::new(),
|
instructions: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,50 +118,80 @@ impl Env {
|
||||||
self.labels.get(label).copied()
|
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 {
|
if let (Token::Op(name, args), loc) = op {
|
||||||
let i = instruction(&name);
|
let i = instruction(&name);
|
||||||
let mut imm = 0u32;
|
let mut imm = 0u32;
|
||||||
let mut regs = vec![0; 4];
|
let mut regs = vec![0; 4];
|
||||||
if args.len() != i.1.len() {
|
if args.len() != i.1.len() {
|
||||||
return Err(RuntimeErr::InvalidOpArity(
|
return Err((
|
||||||
name,
|
RuntimeErr::InvalidOpArity(name, args.len(), i.1.len()),
|
||||||
args.len(),
|
loc,
|
||||||
i.1.len(),
|
None,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = i.1.into_par_iter()
|
let _ =
|
||||||
.enumerate()
|
i.1.clone()
|
||||||
.try_for_each(|(k, v)| match v {
|
.into_iter()
|
||||||
Arg::Immediate => {
|
.enumerate()
|
||||||
if let Token::Immediate(i) = args[k].0 {
|
.try_for_each(|(k, v)| match v {
|
||||||
imm = i as u32;
|
Arg::Immediate => {
|
||||||
Ok(())
|
if let Token::Immediate(i) = args[k].0 {
|
||||||
} else {
|
imm = i as u32;
|
||||||
Err(RuntimeErr::InvalidType("Immediate".to_string(), v.kind()))
|
Ok(())
|
||||||
}
|
} else {
|
||||||
}
|
Err((
|
||||||
Arg::Register => {
|
RuntimeErr::InvalidType("immediate".to_string(), v.kind()),
|
||||||
if let Token::Register(r) = args[k].0 {
|
args[k].1,
|
||||||
regs[k] = self.reg_to_register(&r).unwrap();
|
None,
|
||||||
Ok(())
|
))
|
||||||
} else {
|
|
||||||
Err(RuntimeErr::InvalidType("Register".to_string(), v.kind()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Arg::Memory => {
|
|
||||||
if let Token::Memory(i, r) = args[k].0 {
|
|
||||||
if r.is_some() {
|
|
||||||
regs[k] = self.reg_to_register(&r.unwrap()).unwrap();
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(RuntimeErr::InvalidType("Memory".to_string(), v.kind()))
|
|
||||||
}
|
}
|
||||||
}
|
Arg::Register(id) => {
|
||||||
_ => unimplemented!()
|
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()),
|
||||||
|
args[k].1,
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Arg::Memory => {
|
||||||
|
if let Token::Memory(i, r) = &args[k].0 {
|
||||||
|
if r.is_some() {
|
||||||
|
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()),
|
||||||
|
args[k].1,
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
})?;
|
||||||
Ok(u32::from_str_radix(&with(i, imm, regs).0.to_string(), 2).unwrap())
|
Ok(u32::from_str_radix(&with(i, imm, regs).0.to_string(), 2).unwrap())
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum SyntaxErr {
|
pub enum SyntaxErr {
|
||||||
TraillingComma,
|
|
||||||
/// false for '(' true for ')'
|
/// false for '(' true for ')'
|
||||||
UnmatchedParen(bool),
|
UnmatchedParen(bool),
|
||||||
UnexpectedChar,
|
UnexpectedChar,
|
||||||
|
@ -13,10 +19,9 @@ pub enum SyntaxErr {
|
||||||
impl Display for SyntaxErr {
|
impl Display for SyntaxErr {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
SyntaxErr::TraillingComma => write!(f, "trailling comma"),
|
|
||||||
SyntaxErr::UnmatchedParen(_) => write!(f, "unmatched parenthesis"),
|
SyntaxErr::UnmatchedParen(_) => write!(f, "unmatched parenthesis"),
|
||||||
SyntaxErr::UnexpectedChar => write!(f, "unexpected character"),
|
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"),
|
SyntaxErr::MemoryInvalidRegister => write!(f, "invalid register"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +30,10 @@ impl Display for SyntaxErr {
|
||||||
impl SyntaxErr {
|
impl SyntaxErr {
|
||||||
pub fn note(&self) -> String {
|
pub fn note(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
SyntaxErr::TraillingComma => "remove the final comma".to_string(),
|
SyntaxErr::UnmatchedParen(false) => "add `)` after 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::UnmatchedParen(true) => "add '(' before the register name".to_string(),
|
|
||||||
SyntaxErr::UnexpectedChar => "ensure the input is well-formed".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 => {
|
SyntaxErr::MemoryInvalidRegister => {
|
||||||
"registers are either xN (N < 32 with no leading 0) or the standard aliases"
|
"registers are either xN (N < 32 with no leading 0) or the standard aliases"
|
||||||
.to_string()
|
.to_string()
|
||||||
|
@ -40,29 +44,53 @@ impl SyntaxErr {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum RuntimeErr {
|
pub enum RuntimeErr {
|
||||||
InvalidRegister(String),
|
InvalidOp,
|
||||||
UnexpectedImmediate,
|
/// op, actual, expected
|
||||||
UnexpectedRegister,
|
|
||||||
InvalidOp(String),
|
|
||||||
InvalidOpArity(String, usize, usize),
|
InvalidOpArity(String, usize, usize),
|
||||||
|
/// actual, expected
|
||||||
InvalidType(String, String),
|
InvalidType(String, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for RuntimeErr {
|
impl Display for RuntimeErr {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
RuntimeErr::InvalidRegister(reg) => write!(f, "invalid register {}", reg),
|
RuntimeErr::InvalidOp => write!(f, "invalid operation"),
|
||||||
RuntimeErr::UnexpectedImmediate => write!(f, "unexpected immediate"),
|
RuntimeErr::InvalidOpArity(op, actual, expected) => {
|
||||||
RuntimeErr::UnexpectedRegister => write!(f, "unexpected register"),
|
write!(f, "`{}` expected {} args, got {}", op, expected, actual)
|
||||||
RuntimeErr::InvalidOp(op) => write!(f, "invalid operation {}", op),
|
}
|
||||||
RuntimeErr::InvalidOpArity(op, actual, expected) => write!(
|
|
||||||
f,
|
|
||||||
"invalid operation arity {} expected {} got {}",
|
|
||||||
op, expected, actual
|
|
||||||
),
|
|
||||||
RuntimeErr::InvalidType(actual, expected) => {
|
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 {
|
pub enum Arg {
|
||||||
Register,
|
/// rd -> 0, ra -> 1, rb -> 2, rc -> 3
|
||||||
|
Register(usize),
|
||||||
Immediate,
|
Immediate,
|
||||||
|
/// always ra
|
||||||
Memory,
|
Memory,
|
||||||
Symbol,
|
Symbol,
|
||||||
}
|
}
|
||||||
|
@ -232,7 +235,7 @@ pub enum Arg {
|
||||||
impl Arg {
|
impl Arg {
|
||||||
pub fn kind(&self) -> String {
|
pub fn kind(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Arg::Register => "register",
|
Arg::Register(_) => "register",
|
||||||
Arg::Immediate => "immediate",
|
Arg::Immediate => "immediate",
|
||||||
Arg::Memory => "memory",
|
Arg::Memory => "memory",
|
||||||
Arg::Symbol => "symbol",
|
Arg::Symbol => "symbol",
|
||||||
|
@ -250,14 +253,14 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
|
||||||
"nop" => (Kind::Pseudo(Pseudo {}), vec![]),
|
"nop" => (Kind::Pseudo(Pseudo {}), vec![]),
|
||||||
|
|
||||||
// Move
|
// Move
|
||||||
"li" => (Kind::Pseudo(Pseudo {}), vec![Arg::Register, Arg::Immediate]),
|
"li" => (Kind::Pseudo(Pseudo {}), vec![Arg::Register(0), Arg::Immediate]),
|
||||||
"lui" => (
|
"lui" => (
|
||||||
Kind::U(U {
|
Kind::U(U {
|
||||||
imm: to_bits(0),
|
imm: to_bits(0),
|
||||||
rd: to_bits(0),
|
rd: to_bits(0),
|
||||||
opcode: to_bits(0b0110111),
|
opcode: to_bits(0b0110111),
|
||||||
}),
|
}),
|
||||||
vec![Arg::Register, Arg::Immediate],
|
vec![Arg::Register(0), Arg::Immediate],
|
||||||
),
|
),
|
||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
|
@ -270,7 +273,7 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
|
||||||
imm2: to_bits(0),
|
imm2: to_bits(0),
|
||||||
opcode: to_bits(0b0100011),
|
opcode: to_bits(0b0100011),
|
||||||
}),
|
}),
|
||||||
vec![Arg::Register, Arg::Memory],
|
vec![Arg::Register(2), Arg::Memory],
|
||||||
),
|
),
|
||||||
|
|
||||||
// Arithmetic, Logic, Shift
|
// Arithmetic, Logic, Shift
|
||||||
|
@ -283,7 +286,7 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
|
||||||
rd: to_bits(0),
|
rd: to_bits(0),
|
||||||
opcode: to_bits(0b0110011),
|
opcode: to_bits(0b0110011),
|
||||||
}),
|
}),
|
||||||
vec![Arg::Register, Arg::Register, Arg::Register],
|
vec![Arg::Register(0), Arg::Register(1), Arg::Register(2)],
|
||||||
),
|
),
|
||||||
"addi" => (
|
"addi" => (
|
||||||
Kind::I(I {
|
Kind::I(I {
|
||||||
|
@ -293,7 +296,7 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
|
||||||
rd: to_bits(0),
|
rd: to_bits(0),
|
||||||
opcode: to_bits(0b0010011),
|
opcode: to_bits(0b0010011),
|
||||||
}),
|
}),
|
||||||
vec![Arg::Register, Arg::Register, Arg::Immediate],
|
vec![Arg::Register(0), Arg::Register(1), Arg::Immediate],
|
||||||
),
|
),
|
||||||
// Multiply, Divide
|
// Multiply, Divide
|
||||||
|
|
||||||
|
@ -309,7 +312,7 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
|
||||||
imm2: to_bits(0),
|
imm2: to_bits(0),
|
||||||
opcode: to_bits(0b1100011),
|
opcode: to_bits(0b1100011),
|
||||||
}),
|
}),
|
||||||
vec![Arg::Register, Arg::Register, Arg::Immediate],
|
vec![Arg::Register(1), Arg::Register(2), Arg::Immediate],
|
||||||
),
|
),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
|
|
79
src/main.rs
79
src/main.rs
|
@ -9,8 +9,7 @@ use codespan_reporting::{
|
||||||
};
|
};
|
||||||
use riscv_interpreter::{
|
use riscv_interpreter::{
|
||||||
env::Env,
|
env::Env,
|
||||||
instructions::{instruction, with},
|
parser::{parse, Token},
|
||||||
parser::parse,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
|
@ -24,33 +23,67 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
match parse(&env, &input) {
|
match parse(&env, &input) {
|
||||||
Ok(tokens) => {
|
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) => {
|
Err(errs) => {
|
||||||
for err in errs {
|
let err = errs.first().unwrap();
|
||||||
let start = err.1.start;
|
let start = err.1.start;
|
||||||
let end = err.1.end + 1;
|
let end = err.1.end + 1;
|
||||||
|
|
||||||
let diagnostic = Diagnostic::error()
|
let diagnostic = Diagnostic::error()
|
||||||
.with_message("Syntax Error")
|
.with_message("Syntax Error")
|
||||||
.with_labels(vec![
|
.with_labels(vec![
|
||||||
Label::primary((), start..end).with_message(err.0.to_string())
|
Label::primary((), start..end).with_message(err.0.to_string())
|
||||||
])
|
])
|
||||||
.with_notes({
|
.with_notes({
|
||||||
let mut notes = Vec::new();
|
let mut notes = Vec::new();
|
||||||
if let Some(note) = err.3 {
|
if let Some(note) = &err.3 {
|
||||||
notes.push(note);
|
notes.push(note.to_string());
|
||||||
}
|
}
|
||||||
notes.push(err.0.note());
|
notes.push(err.0.note());
|
||||||
notes
|
notes
|
||||||
});
|
});
|
||||||
|
|
||||||
term::emit(&mut writer.lock(), &config, &file, &diagnostic).unwrap();
|
term::emit(&mut writer.lock(), &config, &file, &diagnostic).unwrap();
|
||||||
}
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,18 +57,14 @@ pub struct Loc {
|
||||||
pub end: usize,
|
pub end: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
|
fn parse_line(env: &Env, input: &str, line: usize, loc: &mut Loc) -> Result<Vec<(Token, Loc)>, ParseErr> {
|
||||||
let mut loc = Loc {
|
|
||||||
line,
|
|
||||||
start: 0,
|
|
||||||
end: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut tokens: Vec<(Token, Loc)> = Vec::new();
|
let mut tokens: Vec<(Token, Loc)> = Vec::new();
|
||||||
let mut chars = input.chars().peekable();
|
let mut chars = input.chars().peekable();
|
||||||
|
|
||||||
use Token::*;
|
use Token::*;
|
||||||
|
|
||||||
|
loc.line = line;
|
||||||
|
|
||||||
while let Some(c) = chars.next() {
|
while let Some(c) = chars.next() {
|
||||||
let token = match c {
|
let token = match c {
|
||||||
'\t' | ' ' => Spacing,
|
'\t' | ' ' => Spacing,
|
||||||
|
@ -87,7 +83,7 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
||||||
num.push(chars.next().unwrap());
|
num.push(chars.next().unwrap());
|
||||||
loc.end += 1;
|
loc.end += 1;
|
||||||
}
|
}
|
||||||
if let Some('(') | Some(' ') = chars.peek() {
|
if let Some('(') | Some(' ') | None = chars.peek() {
|
||||||
Immediate(num.parse().unwrap())
|
Immediate(num.parse().unwrap())
|
||||||
} else {
|
} else {
|
||||||
return Err((
|
return Err((
|
||||||
|
@ -170,8 +166,8 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
||||||
// Opcode or Label definition
|
// Opcode or Label definition
|
||||||
'a'..='z' | 'A'..='Z' | '_' => {
|
'a'..='z' | 'A'..='Z' | '_' => {
|
||||||
let mut str = c.to_string();
|
let mut str = c.to_string();
|
||||||
while let Some('a'..='z') | Some('A'..='Z') | Some('_') | Some('0'..='9') | Some('.') =
|
while let Some('a'..='z') | Some('A'..='Z') | Some('_') | Some('0'..='9')
|
||||||
chars.peek()
|
| Some('.') = chars.peek()
|
||||||
{
|
{
|
||||||
str.push(chars.next().unwrap());
|
str.push(chars.next().unwrap());
|
||||||
loc.end += 1;
|
loc.end += 1;
|
||||||
|
@ -180,34 +176,16 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
||||||
chars.next();
|
chars.next();
|
||||||
loc.end += 1;
|
loc.end += 1;
|
||||||
LabelDef(str[..str.len()].to_string())
|
LabelDef(str[..str.len()].to_string())
|
||||||
} else if let Some((Op(_, _), _)) = tokens.get(tokens.len() - 2) {
|
} else {
|
||||||
// These Registers may actually be label references or symbols, but there's ambiguity
|
// 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
|
// between them and registers, so we'll just assume they're registers for now
|
||||||
Register(str.trim().to_owned())
|
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)),
|
_ => return Err((SyntaxErr::UnexpectedChar, loc.clone(), tokens.clone(), None)),
|
||||||
};
|
};
|
||||||
tokens.push((token, loc.clone()));
|
tokens.push((token, loc.clone()));
|
||||||
loc.end += 1;
|
loc.end += 1; // Newline
|
||||||
loc.start = loc.end;
|
loc.start = loc.end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,10 +193,7 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(token, _)| !matches!(token, Token::Spacing))
|
.filter(|(token, _)| !matches!(token, Token::Spacing))
|
||||||
.group_by(|(token, _)| {
|
.group_by(|(token, _)| {
|
||||||
matches!(
|
matches!(token, Immediate(_) | Register(_) | Memory(_, _) | Symbol(_))
|
||||||
token,
|
|
||||||
Op(_, _) | Immediate(_) | Register(_) | Memory(_, _) | Symbol(_) | String(_)
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|group| {
|
.flat_map(|group| {
|
||||||
|
@ -226,8 +201,8 @@ fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>,
|
||||||
if is_op {
|
if is_op {
|
||||||
let group = group.collect::<Vec<_>>();
|
let group = group.collect::<Vec<_>>();
|
||||||
let (op, loc) = group[0].clone();
|
let (op, loc) = group[0].clone();
|
||||||
let (op, mut args) = match op {
|
let (name, mut args) = match op {
|
||||||
Op(op, args) => (op, args),
|
Register(r) => (r, vec![]),
|
||||||
// because any register/symbol/label def is interpreted as an Op by default, this only
|
// 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
|
// 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..]);
|
args.extend_from_slice(&group[1..]);
|
||||||
|
|
||||||
vec![(Op(op, args), loc)]
|
vec![(Op(name, args), loc)]
|
||||||
} else {
|
} else {
|
||||||
group.collect::<Vec<_>>()
|
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,
|
/// 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
|
/// 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>> {
|
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
|
let parsed_lines = input
|
||||||
.lines()
|
.lines()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.par_bridge()
|
.map(|(i, line)| parse_line(env, line, i + 1, &mut loc))
|
||||||
.map(|(i, line)| parse_line(env, line, i + 1))
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let (ok, err) = parsed_lines
|
let (ok, err): (Vec<_>, Vec<_>) = parsed_lines.into_iter()
|
||||||
.into_par_iter()
|
.partition(|line| matches!(line, Ok(_)));
|
||||||
.partition::<Vec<Result<_, _>>, Vec<Result<_, _>>, _>(|line| matches!(line, Ok(_)));
|
|
||||||
dbg!(&err);
|
dbg!(err.clone());
|
||||||
|
|
||||||
if err.is_empty() {
|
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 {
|
} 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:
|
a:
|
||||||
addi a0 zero 1
|
a0 addi a0 zero 1
|
||||||
addi a1 zero 2
|
addi a1 zero 2
|
||||||
add a2 a1 a0
|
a0 add a2 a1 a0
|
||||||
|
|
Loading…
Reference in a new issue