mirror of
https://github.com/Noratrieb/crapderive.git
synced 2026-01-14 16:45:08 +01:00
cool errors
This commit is contained in:
parent
29219405ff
commit
e6ea89b754
7 changed files with 254 additions and 33 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
|
@ -17,10 +17,20 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ariadne"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c"
|
||||
dependencies = [
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asm-thing"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
"dbg-pls",
|
||||
"insta",
|
||||
"logos",
|
||||
|
|
@ -571,3 +581,9 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
|||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ariadne = "0.1.5"
|
||||
dbg-pls = { version = "0.3.2", features = ["colors", "derive"] }
|
||||
logos = "0.12.1"
|
||||
|
||||
|
|
|
|||
26
src/error.rs
26
src/error.rs
|
|
@ -1,3 +1,4 @@
|
|||
use ariadne::{Color, Label, Report, ReportKind, Source};
|
||||
use dbg_pls::DebugPls;
|
||||
use logos::Span;
|
||||
|
||||
|
|
@ -5,6 +6,31 @@ use logos::Span;
|
|||
pub struct CompilerError {
|
||||
pub msg: String,
|
||||
pub span: Span,
|
||||
pub notes: Vec<(String, Span)>,
|
||||
pub help: Option<String>,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, CompilerError>;
|
||||
|
||||
pub fn report(error: CompilerError, filename: &str, src: &str) {
|
||||
let mut report = Report::build(ReportKind::Error, filename, 12)
|
||||
.with_message(&error.msg)
|
||||
.with_label(
|
||||
Label::new((filename, error.span))
|
||||
.with_message(error.msg)
|
||||
.with_color(Color::Red),
|
||||
);
|
||||
|
||||
for (note, span) in error.notes {
|
||||
report = report.with_label(Label::new((filename, span)).with_message(note));
|
||||
}
|
||||
|
||||
if let Some(help) = error.help {
|
||||
report = report.with_help(help);
|
||||
}
|
||||
|
||||
report
|
||||
.finish()
|
||||
.print((filename, Source::from(src)))
|
||||
.expect("failed to print");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,13 @@ mod parser;
|
|||
fn main() -> Result<(), io::Error> {
|
||||
let file = std::fs::read_to_string("./test.at")?;
|
||||
let result = parser::parse(&file);
|
||||
let _ = dbg_pls::color!(result);
|
||||
|
||||
match result {
|
||||
Ok(ast) => {
|
||||
dbg_pls::color!(ast);
|
||||
}
|
||||
Err(error) => error::report(error, "test.at", &file),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
107
src/parser.rs
107
src/parser.rs
|
|
@ -61,7 +61,7 @@ pub enum StmtKind {
|
|||
Div { to: Expr, value: Expr },
|
||||
Jmp { to: Expr },
|
||||
Je { to: Expr },
|
||||
Cmp { rhs: Expr, lhs: Expr },
|
||||
Cmp { lhs: Expr, rhs: Expr },
|
||||
Label { name: String },
|
||||
}
|
||||
|
||||
|
|
@ -87,21 +87,36 @@ where
|
|||
}
|
||||
|
||||
impl CompilerError {
|
||||
fn new(msg: String, span: Span) -> Self {
|
||||
Self { span, msg }
|
||||
fn new(msg: String, span: Span, notes: Vec<(String, Span)>, help: Option<String>) -> Self {
|
||||
Self {
|
||||
msg,
|
||||
span,
|
||||
notes,
|
||||
help,
|
||||
}
|
||||
}
|
||||
|
||||
fn simple(msg: String, span: Span) -> Self {
|
||||
Self::new_notes(msg, span, Vec::new())
|
||||
}
|
||||
|
||||
fn new_notes(msg: String, span: Span, notes: Vec<(String, Span)>) -> Self {
|
||||
Self::new(msg, span, notes, None)
|
||||
}
|
||||
|
||||
fn not_allowed(span: Span, token: &str) -> Self {
|
||||
Self::new(format!("`{token}` is not allowed here"), span)
|
||||
Self::simple(format!("`{token}` is not allowed here"), span)
|
||||
}
|
||||
|
||||
fn invalid_token(span: Span) -> Self {
|
||||
Self::new("Invalid token".to_string(), span)
|
||||
Self::simple("Invalid token".to_string(), span)
|
||||
}
|
||||
fn eof() -> Self {
|
||||
Self {
|
||||
span: Default::default(),
|
||||
span: 0..0,
|
||||
msg: "Unexpected end of file".to_string(),
|
||||
notes: Vec::new(),
|
||||
help: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -112,13 +127,13 @@ macro_rules! expect {
|
|||
if let $token = next {
|
||||
span
|
||||
} else {
|
||||
return Err(CompilerError {
|
||||
msg: format!(
|
||||
return Err(CompilerError::simple(
|
||||
format!(
|
||||
concat!("Expected ", stringify!($token), ", found {:?}"),
|
||||
next,
|
||||
),
|
||||
span,
|
||||
});
|
||||
));
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
|
@ -137,7 +152,7 @@ where
|
|||
}
|
||||
|
||||
fn stmt(&mut self) -> Result<Stmt> {
|
||||
let stmt = |kind, span| Stmt { kind, span };
|
||||
let stmt = |span, kind| Stmt { kind, span };
|
||||
|
||||
let (token, span) = self.next()?;
|
||||
Ok(match token {
|
||||
|
|
@ -145,60 +160,65 @@ where
|
|||
let to = self.expr()?;
|
||||
expect!(self, Token::Comma);
|
||||
let from = self.expr()?;
|
||||
stmt(StmtKind::Mov { to, from }, Default::default())
|
||||
stmt(span.start..from.span.end, StmtKind::Mov { to, from })
|
||||
}
|
||||
Token::Jmp => {
|
||||
let to = self.expr()?;
|
||||
Stmt {
|
||||
kind: StmtKind::Jmp { to },
|
||||
span: Default::default(),
|
||||
}
|
||||
stmt(span.start..to.span.end, StmtKind::Jmp { to })
|
||||
}
|
||||
Token::Je => {
|
||||
let to = self.expr()?;
|
||||
stmt(StmtKind::Je { to }, Default::default())
|
||||
stmt(span.start..to.span.end, StmtKind::Je { to })
|
||||
}
|
||||
Token::Cmp => {
|
||||
let lhs = self.expr()?;
|
||||
expect!(self, Token::Comma);
|
||||
let rhs = self.expr()?;
|
||||
stmt(StmtKind::Cmp { lhs, rhs }, Default::default())
|
||||
stmt(span.start..rhs.span.end, StmtKind::Cmp { lhs, rhs })
|
||||
}
|
||||
Token::Add => {
|
||||
let to = self.expr()?;
|
||||
expect!(self, Token::Comma);
|
||||
let value = self.expr()?;
|
||||
stmt(StmtKind::Add { to, value }, Default::default())
|
||||
stmt(span.start..value.span.end, StmtKind::Add { to, value })
|
||||
}
|
||||
Token::Sub => {
|
||||
let to = self.expr()?;
|
||||
expect!(self, Token::Comma);
|
||||
let value = self.expr()?;
|
||||
stmt(StmtKind::Sub { to, value }, Default::default())
|
||||
stmt(span.start..value.span.end, StmtKind::Sub { to, value })
|
||||
}
|
||||
Token::Mul => {
|
||||
let to = self.expr()?;
|
||||
expect!(self, Token::Comma);
|
||||
let value = self.expr()?;
|
||||
stmt(StmtKind::Mul { to, value }, Default::default())
|
||||
stmt(span.start..value.span.end, StmtKind::Mul { to, value })
|
||||
}
|
||||
Token::Div => {
|
||||
let to = self.expr()?;
|
||||
expect!(self, Token::Comma);
|
||||
let value = self.expr()?;
|
||||
stmt(StmtKind::Div { to, value }, Default::default())
|
||||
stmt(span.start..value.span.end, StmtKind::Div { to, value })
|
||||
}
|
||||
Token::Label(name) => {
|
||||
let name = name
|
||||
.strip_suffix(":")
|
||||
.expect("lexer produced invalid label")
|
||||
.to_owned();
|
||||
stmt(span, StmtKind::Label { name })
|
||||
}
|
||||
Token::Label(name) => stmt(
|
||||
StmtKind::Label {
|
||||
name: name.to_owned(),
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
Token::BracketOpen => return Err(CompilerError::not_allowed(span, "[")),
|
||||
Token::BracketClose => return Err(CompilerError::not_allowed(span, "]")),
|
||||
Token::Comma => return Err(CompilerError::not_allowed(span, ",")),
|
||||
Token::Number(_) => return Err(CompilerError::not_allowed(span, "{number}")),
|
||||
Token::Word(_) => return Err(CompilerError::not_allowed(span, "{word}")),
|
||||
Token::Word(word) => {
|
||||
return Err(CompilerError::new(
|
||||
"{word}".to_string(),
|
||||
span.clone(),
|
||||
vec![],
|
||||
Some(format!("Consider using a label instead: `{}:`", word)),
|
||||
))
|
||||
}
|
||||
Token::Error => return Err(CompilerError::invalid_token(span)),
|
||||
})
|
||||
}
|
||||
|
|
@ -219,8 +239,10 @@ where
|
|||
if let Ok(n) = r_number.parse::<u8>() {
|
||||
if n > 15 {
|
||||
return Err(CompilerError::new(
|
||||
format!("Only registers from 0..15 are available. Invalid register: {n}"),
|
||||
span,
|
||||
format!("Invalid register number: {n}"),
|
||||
span.clone(),
|
||||
vec![("Registers available: r0..r15".to_owned(), span)],
|
||||
None,
|
||||
));
|
||||
}
|
||||
return Ok(expr(ExprKind::Register(n), span));
|
||||
|
|
@ -261,4 +283,27 @@ pub fn parse(src: &str) -> Result<Vec<Stmt>> {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
mod tests {
|
||||
#[test]
|
||||
fn program() {
|
||||
let result = super::parse(
|
||||
"
|
||||
mov r0, 3
|
||||
cmp r0, 8
|
||||
je true
|
||||
jmp false
|
||||
true:
|
||||
jmp exit
|
||||
|
||||
// loop
|
||||
false:
|
||||
mov r1, [8]
|
||||
jmp false
|
||||
|
||||
|
||||
exit:
|
||||
",
|
||||
);
|
||||
insta::assert_debug_snapshot!(result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
126
src/snapshots/asm_thing__parser__tests__program.snap
Normal file
126
src/snapshots/asm_thing__parser__tests__program.snap
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
---
|
||||
source: src/parser.rs
|
||||
expression: result
|
||||
---
|
||||
Ok(
|
||||
[
|
||||
Stmt {
|
||||
kind: Mov {
|
||||
to: Expr {
|
||||
kind: Register(
|
||||
0,
|
||||
),
|
||||
span: 5..7,
|
||||
},
|
||||
from: Expr {
|
||||
kind: Number(
|
||||
3,
|
||||
),
|
||||
span: 9..10,
|
||||
},
|
||||
},
|
||||
span: 1..10,
|
||||
},
|
||||
Stmt {
|
||||
kind: Cmp {
|
||||
lhs: Expr {
|
||||
kind: Register(
|
||||
0,
|
||||
),
|
||||
span: 15..17,
|
||||
},
|
||||
rhs: Expr {
|
||||
kind: Number(
|
||||
8,
|
||||
),
|
||||
span: 19..20,
|
||||
},
|
||||
},
|
||||
span: 11..20,
|
||||
},
|
||||
Stmt {
|
||||
kind: Je {
|
||||
to: Expr {
|
||||
kind: Name(
|
||||
"true",
|
||||
),
|
||||
span: 24..28,
|
||||
},
|
||||
},
|
||||
span: 21..28,
|
||||
},
|
||||
Stmt {
|
||||
kind: Jmp {
|
||||
to: Expr {
|
||||
kind: Name(
|
||||
"false",
|
||||
),
|
||||
span: 33..38,
|
||||
},
|
||||
},
|
||||
span: 29..38,
|
||||
},
|
||||
Stmt {
|
||||
kind: Label {
|
||||
name: "true",
|
||||
},
|
||||
span: 39..44,
|
||||
},
|
||||
Stmt {
|
||||
kind: Jmp {
|
||||
to: Expr {
|
||||
kind: Name(
|
||||
"exit",
|
||||
),
|
||||
span: 49..53,
|
||||
},
|
||||
},
|
||||
span: 45..53,
|
||||
},
|
||||
Stmt {
|
||||
kind: Label {
|
||||
name: "false",
|
||||
},
|
||||
span: 63..69,
|
||||
},
|
||||
Stmt {
|
||||
kind: Mov {
|
||||
to: Expr {
|
||||
kind: Register(
|
||||
1,
|
||||
),
|
||||
span: 74..76,
|
||||
},
|
||||
from: Expr {
|
||||
kind: Addr(
|
||||
Expr {
|
||||
kind: Number(
|
||||
8,
|
||||
),
|
||||
span: 79..80,
|
||||
},
|
||||
),
|
||||
span: 78..81,
|
||||
},
|
||||
},
|
||||
span: 70..81,
|
||||
},
|
||||
Stmt {
|
||||
kind: Jmp {
|
||||
to: Expr {
|
||||
kind: Name(
|
||||
"false",
|
||||
),
|
||||
span: 86..91,
|
||||
},
|
||||
},
|
||||
span: 82..91,
|
||||
},
|
||||
Stmt {
|
||||
kind: Label {
|
||||
name: "exit",
|
||||
},
|
||||
span: 94..99,
|
||||
},
|
||||
],
|
||||
)
|
||||
2
test.at
2
test.at
|
|
@ -7,7 +7,7 @@ jmp exit
|
|||
|
||||
// loop
|
||||
false:
|
||||
mov r1, [8]
|
||||
mov r5, [8]
|
||||
jmp false
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue