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.
|
# 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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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);
|
||||||
|
|
15
src/err.rs
15
src/err.rs
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
53
src/main.rs
53
src/main.rs
|
@ -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());
|
||||||
println!("{:#?} -> {:#?}", input, tokens);
|
|
||||||
|
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
|
/// 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
2
test.s
|
@ -1 +1 @@
|
||||||
a: lw a0 1(sp)
|
lw a0 -4(sp)
|
||||||
|
|
Loading…
Reference in a new issue