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.
version = 3
[[package]]
name = "anyhow"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "codespan-reporting"
version = "0.11.1"
@ -76,6 +82,7 @@ dependencies = [
name = "riscv_interpreter"
version = "0.1.0"
dependencies = [
"anyhow",
"codespan-reporting",
"itertools",
"rayon",

View file

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

View file

@ -2,7 +2,7 @@ use std::collections::HashMap;
#[derive(Debug)]
pub struct Env {
register_alias: HashMap<String, usize>,
pub register_alias: HashMap<String, usize>,
labels: HashMap<String, usize>,
registers: [i64; 32],
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> {
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) {
self.labels.insert(label.to_string(), value);

View file

@ -7,6 +7,7 @@ pub enum SyntaxErr {
UnmatchedParen(bool),
UnexpectedChar,
OutsideOp(String),
MemoryInvalidRegister,
}
impl Display for SyntaxErr {
@ -16,6 +17,20 @@ impl Display for SyntaxErr {
SyntaxErr::UnmatchedParen(_) => write!(f, "unmatched parenthesis"),
SyntaxErr::UnexpectedChar => write!(f, "unexpected character"),
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 riscv_interpreter::parser::{parse, Token};
use codespan_reporting::{
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 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);
}
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
use crate::err::SyntaxErr;
use crate::{env::Env, err::SyntaxErr};
use itertools::Itertools;
use rayon::prelude::*;
@ -58,7 +58,7 @@ pub struct Loc {
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 {
line,
col: 1,
@ -104,6 +104,9 @@ fn parse_line(input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
Immediate(num.parse().unwrap())
}
'(' => {
let start = loc.start + 1;
let col = loc.col + 1;
let imm;
if let Some((Immediate(_), _)) = tokens.last() {
imm = Box::new(tokens.pop().unwrap());
@ -123,6 +126,23 @@ fn parse_line(input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
chars.peek()
{
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(')') {
return Err((
@ -179,7 +199,7 @@ fn parse_line(input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
loc.start = loc.end;
}
Ok(tokens
let tokens = tokens
.into_iter()
.filter(|(token, _)| !matches!(token, Token::Spacing))
.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, mut args) = match op {
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![(
Token::Error((
@ -215,7 +237,14 @@ fn parse_line(input: &str, line: usize) -> Result<Vec<(Token, Loc)>, ParseErr> {
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
@ -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
/// containing the error, the location of the error, the tokens parsed up to that point,
/// 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
.lines()
.enumerate()
.par_bridge()
.map(|(i, line)| parse_line(line, i + 1))
.map(|(i, line)| parse_line(env, line, i + 1))
.collect::<Vec<_>>();
let (ok, err) = parsed_lines

2
test.s
View file

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