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",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ariadne"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c"
|
||||||
|
dependencies = [
|
||||||
|
"yansi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "asm-thing"
|
name = "asm-thing"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ariadne",
|
||||||
"dbg-pls",
|
"dbg-pls",
|
||||||
"insta",
|
"insta",
|
||||||
"logos",
|
"logos",
|
||||||
|
|
@ -571,3 +581,9 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"linked-hash-map",
|
"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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
ariadne = "0.1.5"
|
||||||
dbg-pls = { version = "0.3.2", features = ["colors", "derive"] }
|
dbg-pls = { version = "0.3.2", features = ["colors", "derive"] }
|
||||||
logos = "0.12.1"
|
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 dbg_pls::DebugPls;
|
||||||
use logos::Span;
|
use logos::Span;
|
||||||
|
|
||||||
|
|
@ -5,6 +6,31 @@ use logos::Span;
|
||||||
pub struct CompilerError {
|
pub struct CompilerError {
|
||||||
pub msg: String,
|
pub msg: String,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
|
pub notes: Vec<(String, Span)>,
|
||||||
|
pub help: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, CompilerError>;
|
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> {
|
fn main() -> Result<(), io::Error> {
|
||||||
let file = std::fs::read_to_string("./test.at")?;
|
let file = std::fs::read_to_string("./test.at")?;
|
||||||
let result = parser::parse(&file);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
107
src/parser.rs
107
src/parser.rs
|
|
@ -61,7 +61,7 @@ pub enum StmtKind {
|
||||||
Div { to: Expr, value: Expr },
|
Div { to: Expr, value: Expr },
|
||||||
Jmp { to: Expr },
|
Jmp { to: Expr },
|
||||||
Je { to: Expr },
|
Je { to: Expr },
|
||||||
Cmp { rhs: Expr, lhs: Expr },
|
Cmp { lhs: Expr, rhs: Expr },
|
||||||
Label { name: String },
|
Label { name: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,21 +87,36 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompilerError {
|
impl CompilerError {
|
||||||
fn new(msg: String, span: Span) -> Self {
|
fn new(msg: String, span: Span, notes: Vec<(String, Span)>, help: Option<String>) -> Self {
|
||||||
Self { span, msg }
|
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 {
|
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 {
|
fn invalid_token(span: Span) -> Self {
|
||||||
Self::new("Invalid token".to_string(), span)
|
Self::simple("Invalid token".to_string(), span)
|
||||||
}
|
}
|
||||||
fn eof() -> Self {
|
fn eof() -> Self {
|
||||||
Self {
|
Self {
|
||||||
span: Default::default(),
|
span: 0..0,
|
||||||
msg: "Unexpected end of file".to_string(),
|
msg: "Unexpected end of file".to_string(),
|
||||||
|
notes: Vec::new(),
|
||||||
|
help: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -112,13 +127,13 @@ macro_rules! expect {
|
||||||
if let $token = next {
|
if let $token = next {
|
||||||
span
|
span
|
||||||
} else {
|
} else {
|
||||||
return Err(CompilerError {
|
return Err(CompilerError::simple(
|
||||||
msg: format!(
|
format!(
|
||||||
concat!("Expected ", stringify!($token), ", found {:?}"),
|
concat!("Expected ", stringify!($token), ", found {:?}"),
|
||||||
next,
|
next,
|
||||||
),
|
),
|
||||||
span,
|
span,
|
||||||
});
|
));
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +152,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stmt(&mut self) -> Result<Stmt> {
|
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()?;
|
let (token, span) = self.next()?;
|
||||||
Ok(match token {
|
Ok(match token {
|
||||||
|
|
@ -145,60 +160,65 @@ where
|
||||||
let to = self.expr()?;
|
let to = self.expr()?;
|
||||||
expect!(self, Token::Comma);
|
expect!(self, Token::Comma);
|
||||||
let from = self.expr()?;
|
let from = self.expr()?;
|
||||||
stmt(StmtKind::Mov { to, from }, Default::default())
|
stmt(span.start..from.span.end, StmtKind::Mov { to, from })
|
||||||
}
|
}
|
||||||
Token::Jmp => {
|
Token::Jmp => {
|
||||||
let to = self.expr()?;
|
let to = self.expr()?;
|
||||||
Stmt {
|
stmt(span.start..to.span.end, StmtKind::Jmp { to })
|
||||||
kind: StmtKind::Jmp { to },
|
|
||||||
span: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Token::Je => {
|
Token::Je => {
|
||||||
let to = self.expr()?;
|
let to = self.expr()?;
|
||||||
stmt(StmtKind::Je { to }, Default::default())
|
stmt(span.start..to.span.end, StmtKind::Je { to })
|
||||||
}
|
}
|
||||||
Token::Cmp => {
|
Token::Cmp => {
|
||||||
let lhs = self.expr()?;
|
let lhs = self.expr()?;
|
||||||
expect!(self, Token::Comma);
|
expect!(self, Token::Comma);
|
||||||
let rhs = self.expr()?;
|
let rhs = self.expr()?;
|
||||||
stmt(StmtKind::Cmp { lhs, rhs }, Default::default())
|
stmt(span.start..rhs.span.end, StmtKind::Cmp { lhs, rhs })
|
||||||
}
|
}
|
||||||
Token::Add => {
|
Token::Add => {
|
||||||
let to = self.expr()?;
|
let to = self.expr()?;
|
||||||
expect!(self, Token::Comma);
|
expect!(self, Token::Comma);
|
||||||
let value = self.expr()?;
|
let value = self.expr()?;
|
||||||
stmt(StmtKind::Add { to, value }, Default::default())
|
stmt(span.start..value.span.end, StmtKind::Add { to, value })
|
||||||
}
|
}
|
||||||
Token::Sub => {
|
Token::Sub => {
|
||||||
let to = self.expr()?;
|
let to = self.expr()?;
|
||||||
expect!(self, Token::Comma);
|
expect!(self, Token::Comma);
|
||||||
let value = self.expr()?;
|
let value = self.expr()?;
|
||||||
stmt(StmtKind::Sub { to, value }, Default::default())
|
stmt(span.start..value.span.end, StmtKind::Sub { to, value })
|
||||||
}
|
}
|
||||||
Token::Mul => {
|
Token::Mul => {
|
||||||
let to = self.expr()?;
|
let to = self.expr()?;
|
||||||
expect!(self, Token::Comma);
|
expect!(self, Token::Comma);
|
||||||
let value = self.expr()?;
|
let value = self.expr()?;
|
||||||
stmt(StmtKind::Mul { to, value }, Default::default())
|
stmt(span.start..value.span.end, StmtKind::Mul { to, value })
|
||||||
}
|
}
|
||||||
Token::Div => {
|
Token::Div => {
|
||||||
let to = self.expr()?;
|
let to = self.expr()?;
|
||||||
expect!(self, Token::Comma);
|
expect!(self, Token::Comma);
|
||||||
let value = self.expr()?;
|
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::BracketOpen => return Err(CompilerError::not_allowed(span, "[")),
|
||||||
Token::BracketClose => return Err(CompilerError::not_allowed(span, "]")),
|
Token::BracketClose => return Err(CompilerError::not_allowed(span, "]")),
|
||||||
Token::Comma => return Err(CompilerError::not_allowed(span, ",")),
|
Token::Comma => return Err(CompilerError::not_allowed(span, ",")),
|
||||||
Token::Number(_) => return Err(CompilerError::not_allowed(span, "{number}")),
|
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)),
|
Token::Error => return Err(CompilerError::invalid_token(span)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -219,8 +239,10 @@ where
|
||||||
if let Ok(n) = r_number.parse::<u8>() {
|
if let Ok(n) = r_number.parse::<u8>() {
|
||||||
if n > 15 {
|
if n > 15 {
|
||||||
return Err(CompilerError::new(
|
return Err(CompilerError::new(
|
||||||
format!("Only registers from 0..15 are available. Invalid register: {n}"),
|
format!("Invalid register number: {n}"),
|
||||||
span,
|
span.clone(),
|
||||||
|
vec![("Registers available: r0..r15".to_owned(), span)],
|
||||||
|
None,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return Ok(expr(ExprKind::Register(n), span));
|
return Ok(expr(ExprKind::Register(n), span));
|
||||||
|
|
@ -261,4 +283,27 @@ pub fn parse(src: &str) -> Result<Vec<Stmt>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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
|
// loop
|
||||||
false:
|
false:
|
||||||
mov r1, [8]
|
mov r5, [8]
|
||||||
jmp false
|
jmp false
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue