Squashed bugs, added some error reports
This commit is contained in:
parent
fad3e6d361
commit
9a931b4b8f
7 changed files with 115 additions and 15 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
15
src/err.rs
15
src/err.rs
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
53
src/main.rs
53
src/main.rs
|
@ -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<_>>());
|
||||
println!("{:#?} -> {:#?}", input, tokens);
|
||||
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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
2
test.s
|
@ -1 +1 @@
|
|||
a: lw a0 1(sp)
|
||||
lw a0 -4(sp)
|
||||
|
|
Loading…
Reference in a new issue