Squashed bugs, added some error reports

This commit is contained in:
Lumi Kalt 2024-01-21 12:36:38 +00:00
parent fad3e6d361
commit 9a931b4b8f
7 changed files with 115 additions and 15 deletions

7
Cargo.lock generated
View file

@ -2,6 +2,12 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "anyhow"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]] [[package]]
name = "codespan-reporting" name = "codespan-reporting"
version = "0.11.1" version = "0.11.1"
@ -76,6 +82,7 @@ dependencies = [
name = "riscv_interpreter" name = "riscv_interpreter"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"codespan-reporting", "codespan-reporting",
"itertools", "itertools",
"rayon", "rayon",

View file

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
codespan-reporting = { version = "0.11.1", features = ["ascii-only"] } anyhow = "1.0.79"
codespan-reporting = "0.11.1"
itertools = "0.12.0" itertools = "0.12.0"
rayon = "1.8.1" rayon = "1.8.1"

View file

@ -2,7 +2,7 @@ use std::collections::HashMap;
#[derive(Debug)] #[derive(Debug)]
pub struct Env { pub struct Env {
register_alias: HashMap<String, usize>, pub register_alias: HashMap<String, usize>,
labels: HashMap<String, usize>, labels: HashMap<String, usize>,
registers: [i64; 32], registers: [i64; 32],
stack: Vec<i64>, // TODO: Find the size of the stack stack: Vec<i64>, // TODO: Find the size of the stack
@ -68,6 +68,13 @@ impl Env {
pub fn alias_to_register(&self, reg: &str) -> Option<usize> { pub fn alias_to_register(&self, reg: &str) -> Option<usize> {
self.register_alias.get(reg).copied() self.register_alias.get(reg).copied()
} }
pub fn xn_to_register(&self, reg: &str) -> Option<usize> {
if reg.starts_with("x") {
reg[1..].parse::<usize>().ok()
} else {
None
}
}
pub fn add_label(&mut self, label: &str, value: usize) { pub fn add_label(&mut self, label: &str, value: usize) {
self.labels.insert(label.to_string(), value); self.labels.insert(label.to_string(), value);

View file

@ -7,6 +7,7 @@ pub enum SyntaxErr {
UnmatchedParen(bool), UnmatchedParen(bool),
UnexpectedChar, UnexpectedChar,
OutsideOp(String), OutsideOp(String),
MemoryInvalidRegister,
} }
impl Display for SyntaxErr { impl Display for SyntaxErr {
@ -16,6 +17,20 @@ impl Display for SyntaxErr {
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"),
}
}
}
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::UnexpectedChar => "ensure the input is well-formed".to_string(),
SyntaxErr::OutsideOp(kind) => format!("add '{}'s only after an opcode", kind),
SyntaxErr::MemoryInvalidRegister => "valid registers are of the form xN, 0 <= N < 32, or the standard aliases".to_string(),
} }
} }
} }

View file

@ -1,9 +1,50 @@
use rayon::prelude::*; use codespan_reporting::{
use riscv_interpreter::parser::{parse, Token}; diagnostic::{self, Diagnostic, Label},
files::SimpleFile,
term::{
self,
termcolor::{ColorChoice, StandardStream},
Config,
},
};
use riscv_interpreter::{env::Env, parser::parse};
fn main() { fn main() -> anyhow::Result<()> {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = Config::default();
let input = std::fs::read_to_string("test.s").unwrap(); let input = std::fs::read_to_string("test.s").unwrap();
let tokens = parse(&input);
// println!("{:#?} -> {:#?}", input, tokens.into_par_iter().filter(|(token, _)| !matches!(token, Token::Spacing)).collect::<Vec<_>>()); let file = SimpleFile::new("test.s", input.clone());
let env = Env::new();
match parse(&env, &input) {
Ok(tokens) => {
println!("{:#?} -> {:#?}", input, tokens); println!("{:#?} -> {:#?}", input, tokens);
} }
Err(errs) => {
for err in errs {
let start = err.1.start;
let end = err.1.end;
let diagnostic = Diagnostic::error()
.with_message("Syntax Error")
.with_labels(vec![
Label::primary((), start..end).with_message(err.0.to_string())
])
.with_notes({
let mut notes = Vec::new();
if let Some(note) = err.3 {
notes.push(note);
}
notes.push(err.0.note());
notes
});
term::emit(&mut writer.lock(), &config, &file, &diagnostic).unwrap();
}
}
};
Ok(())
}

View file

@ -1,5 +1,5 @@
/// TODO: Strings, Symbols /// TODO: Strings, Symbols
use crate::err::SyntaxErr; use crate::{env::Env, err::SyntaxErr};
use itertools::Itertools; use itertools::Itertools;
use rayon::prelude::*; use rayon::prelude::*;
@ -58,7 +58,7 @@ pub struct Loc {
pub end: usize, pub end: usize,
} }
fn parse_line(input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> { fn parse_line(env: &Env, input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
let mut loc = Loc { let mut loc = Loc {
line, line,
col: 1, col: 1,
@ -104,6 +104,9 @@ fn parse_line(input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
Immediate(num.parse().unwrap()) Immediate(num.parse().unwrap())
} }
'(' => { '(' => {
let start = loc.start + 1;
let col = loc.col + 1;
let imm; let imm;
if let Some((Immediate(_), _)) = tokens.last() { if let Some((Immediate(_), _)) = tokens.last() {
imm = Box::new(tokens.pop().unwrap()); imm = Box::new(tokens.pop().unwrap());
@ -123,6 +126,23 @@ fn parse_line(input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
chars.peek() chars.peek()
{ {
reg.push(chars.next().unwrap()); reg.push(chars.next().unwrap());
loc.end += 1;
}
let end = loc.end + 1;
let reg = reg.trim();
if env.alias_to_register(reg).is_none() && env.xn_to_register(reg).is_none() {
return Err((
SyntaxErr::MemoryInvalidRegister,
Loc {
line,
col,
start,
end,
},
tokens.clone(),
None,
));
} }
if chars.next() != Some(')') { if chars.next() != Some(')') {
return Err(( return Err((
@ -179,7 +199,7 @@ fn parse_line(input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
loc.start = loc.end; loc.start = loc.end;
} }
Ok(tokens let tokens = tokens
.into_iter() .into_iter()
.filter(|(token, _)| !matches!(token, Token::Spacing)) .filter(|(token, _)| !matches!(token, Token::Spacing))
.group_by(|(token, _)| { .group_by(|(token, _)| {
@ -196,6 +216,8 @@ fn parse_line(input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
let (op, loc) = dbg!(dbg!(group[0].clone())); let (op, loc) = dbg!(dbg!(group[0].clone()));
let (op, mut args) = match op { let (op, mut args) = match op {
Op(op, args) => (op, args), Op(op, args) => (op, args),
// 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
_ => { _ => {
return vec![( return vec![(
Token::Error(( Token::Error((
@ -215,7 +237,14 @@ fn parse_line(input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
group.collect::<Vec<_>>() group.collect::<Vec<_>>()
} }
}) })
.collect::<Vec<_>>()) .collect::<Vec<_>>();
if let Some((Token::Error(err), _)) =
tokens.iter().find(|line| matches!(line.0, Token::Error(_)))
{
Err(err.to_owned())
} else {
Ok(tokens)
}
} }
/// Parse the input /// Parse the input
@ -223,12 +252,12 @@ fn parse_line(input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
/// Returns a vector of tokens and their locations, if successful, or an error vector /// Returns a vector of tokens and their locations, if successful, or an error vector
/// 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 /// and an optional message to display to the users
pub fn parse(input: &str) -> Result<Vec<(Token, Loc)>, Vec<ParseErr>> { pub fn parse(env: &Env, input: &str) -> Result<Vec<(Token, Loc)>, Vec<ParseErr>> {
let parsed_lines = input let parsed_lines = input
.lines() .lines()
.enumerate() .enumerate()
.par_bridge() .par_bridge()
.map(|(i, line)| parse_line(line, i + 1)) .map(|(i, line)| parse_line(env, line, i + 1))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let (ok, err) = parsed_lines let (ok, err) = parsed_lines

2
test.s
View file

@ -1 +1 @@
a: lw a0 1(sp) lw a0 -4(sp)