Some fancy error
This commit is contained in:
parent
8fcf815dbf
commit
9ebc313027
4 changed files with 206 additions and 45 deletions
|
|
@ -8,3 +8,4 @@ ray-tracing-core = { path = "../ray-tracing-core" }
|
|||
clap = { version = "4.5.41", features = ["derive"] }
|
||||
miette = { version = "7.6.0", features = ["fancy"] }
|
||||
nom = "8.0.0"
|
||||
thiserror = "2.0.12"
|
||||
|
|
|
|||
|
|
@ -33,3 +33,4 @@ AttributeBegin
|
|||
"point2 uv" [ 0 0 1 0 1 1 0 1 ]
|
||||
AttributeEnd
|
||||
|
||||
" fd
|
||||
|
|
|
|||
|
|
@ -1,23 +1,151 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
io::{BufRead, BufReader, Bytes, Read},
|
||||
io::{BufRead, BufReader, Bytes, Read, Seek, Write},
|
||||
iter::Peekable,
|
||||
os::unix::fs::FileExt,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use miette::{Diagnostic, Error, IntoDiagnostic, Result, bail, miette};
|
||||
use miette::{
|
||||
Diagnostic, Error, IntoDiagnostic, Result, SourceCode, SourceSpan, SpanContents, bail, miette,
|
||||
};
|
||||
use nom::IResult;
|
||||
use ray_tracing_core::{
|
||||
affine_transform::AffineTransform,
|
||||
math::{Dir3, Pos3},
|
||||
prelude::Float,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
struct Tokenizer<I: Iterator<Item = Result<char>>> {
|
||||
#[derive(Debug)]
|
||||
struct SourceFile {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl SourceCode for SourceFile {
|
||||
fn read_span<'a>(
|
||||
&'a self,
|
||||
span: &miette::SourceSpan,
|
||||
context_lines_before: usize,
|
||||
context_lines_after: usize,
|
||||
) -> std::result::Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
|
||||
let mut file = std::fs::File::open(&self.path)?;
|
||||
|
||||
// read keeping the last
|
||||
|
||||
let mut data = Vec::new();
|
||||
|
||||
file.read_to_end(&mut data)?;
|
||||
|
||||
let data = data.leak();
|
||||
|
||||
let line = data.iter().filter(|&&x| x == b'\n').count();
|
||||
|
||||
let mut start_new_lines = 0;
|
||||
|
||||
let start_pos = data[0..span.offset()]
|
||||
.iter()
|
||||
.rposition(|&x| {
|
||||
if x == b'\n' {
|
||||
if start_new_lines > context_lines_before {
|
||||
true
|
||||
} else {
|
||||
start_new_lines += 1;
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.map(|x| x + 1)
|
||||
.unwrap_or(0);
|
||||
|
||||
let mut end_new_lines = 0;
|
||||
|
||||
let end_pos = data[span.offset()..]
|
||||
.iter()
|
||||
.position(|&x| {
|
||||
if x == b'\n' {
|
||||
if end_new_lines >= context_lines_after {
|
||||
true
|
||||
} else {
|
||||
end_new_lines += 1;
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.map(|x| x + span.offset())
|
||||
.unwrap_or(data[span.offset()..].len());
|
||||
|
||||
let compact_data = &data[start_pos..end_pos];
|
||||
|
||||
Ok(Box::new(SourceFileSpanContents {
|
||||
span: SourceSpan::new(start_pos.into(), end_pos - start_pos),
|
||||
start_line_number: line - start_new_lines,
|
||||
line_count: start_new_lines + 1 + end_new_lines,
|
||||
path: self.path.clone(),
|
||||
data: compact_data,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SourceFileSpanContents {
|
||||
data: &'static [u8],
|
||||
span: SourceSpan,
|
||||
start_line_number: usize,
|
||||
line_count: usize,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl<'a> SpanContents<'a> for SourceFileSpanContents {
|
||||
fn data(&'_ self) -> &'a [u8] {
|
||||
self.data
|
||||
}
|
||||
|
||||
fn span(&self) -> &SourceSpan {
|
||||
&self.span
|
||||
}
|
||||
|
||||
fn line(&self) -> usize {
|
||||
self.start_line_number
|
||||
}
|
||||
|
||||
fn column(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn line_count(&self) -> usize {
|
||||
self.line_count
|
||||
}
|
||||
|
||||
fn name(&self) -> Option<&str> {
|
||||
self.path.to_str()
|
||||
}
|
||||
|
||||
fn language(&self) -> Option<&str> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, Diagnostic)]
|
||||
#[error("oops!")]
|
||||
#[diagnostic(help("justus ist doof"))]
|
||||
struct MyBad {
|
||||
#[source_code]
|
||||
src: SourceFile,
|
||||
|
||||
#[label("Here")]
|
||||
bad_bit: SourceSpan,
|
||||
}
|
||||
|
||||
struct Tokenizer<I: Iterator<Item = Result<(usize, char)>>> {
|
||||
input_iterator: Peekable<I>,
|
||||
}
|
||||
|
||||
impl<I: Iterator<Item = Result<char>>> Tokenizer<I> {
|
||||
impl<I: Iterator<Item = Result<(usize, char)>>> Tokenizer<I> {
|
||||
fn new(input_iterator: I) -> Self {
|
||||
Self {
|
||||
input_iterator: input_iterator.peekable(),
|
||||
|
|
@ -27,25 +155,24 @@ impl<I: Iterator<Item = Result<char>>> Tokenizer<I> {
|
|||
|
||||
impl<I> Iterator for Tokenizer<I>
|
||||
where
|
||||
I: Iterator<Item = Result<char>>,
|
||||
I: Iterator<Item = Result<(usize, char)>>,
|
||||
{
|
||||
type Item = Result<String>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while self
|
||||
.input_iterator
|
||||
.peek()
|
||||
.is_some_and(|c| c.as_ref().is_ok_and(|&c| c.is_whitespace() || c == '#'))
|
||||
{
|
||||
while self.input_iterator.peek().is_some_and(|c| {
|
||||
c.as_ref()
|
||||
.is_ok_and(|&(_, c)| c.is_whitespace() || c == '#')
|
||||
}) {
|
||||
if self
|
||||
.input_iterator
|
||||
.peek()
|
||||
.is_some_and(|c| c.as_ref().is_ok_and(|&c| c == '#'))
|
||||
.is_some_and(|c| c.as_ref().is_ok_and(|&(_, c)| c == '#'))
|
||||
{
|
||||
while self
|
||||
.input_iterator
|
||||
.next()
|
||||
.is_some_and(|c| c.is_ok_and(|c| c != '\n'))
|
||||
.is_some_and(|c| c.is_ok_and(|(_, c)| c != '\n'))
|
||||
{}
|
||||
} else {
|
||||
match self.input_iterator.next()? {
|
||||
|
|
@ -56,38 +183,43 @@ where
|
|||
}
|
||||
|
||||
match self.input_iterator.next() {
|
||||
Some(Ok('[')) => Some(Ok(String::from('['))),
|
||||
Some(Ok(']')) => Some(Ok(String::from(']'))),
|
||||
Some(Ok('"')) => {
|
||||
Some(Ok((_, '['))) => Some(Ok(String::from('['))),
|
||||
Some(Ok((_, ']'))) => Some(Ok(String::from(']'))),
|
||||
Some(Ok((_, '"'))) => {
|
||||
let mut r = String::from('"');
|
||||
while let Some(p) = self
|
||||
.input_iterator
|
||||
.next_if(|c| c.as_ref().is_ok_and(|&c| c != '"'))
|
||||
.next_if(|c| c.as_ref().is_ok_and(|&(_, c)| c != '"'))
|
||||
{
|
||||
match p {
|
||||
Ok(c) => r.push(c),
|
||||
Ok((_, c)) => r.push(c),
|
||||
Err(e) => return Some(Err(e)),
|
||||
}
|
||||
}
|
||||
if self
|
||||
.input_iterator
|
||||
.next()
|
||||
.is_none_or(|c| !c.is_ok_and(|c| c == '"'))
|
||||
.is_none_or(|c| !c.is_ok_and(|(_, c)| c == '"'))
|
||||
{
|
||||
return Some(Err(miette::miette!("unfinished string")));
|
||||
return Some(Err(From::from(MyBad {
|
||||
src: SourceFile {
|
||||
path: PathBuf::from("ray-tracing-pbrt-scene/example.pbrt"),
|
||||
},
|
||||
bad_bit: SourceSpan::new(40.into(), 4),
|
||||
})));
|
||||
};
|
||||
r.push('"');
|
||||
Some(Ok(r))
|
||||
}
|
||||
Some(Ok(c)) => {
|
||||
Some(Ok((_, c))) => {
|
||||
let mut r = String::new();
|
||||
r.push(c);
|
||||
while let Some(p) = self.input_iterator.next_if(|c| {
|
||||
c.as_ref()
|
||||
.is_ok_and(|&c| c != '#' && c != '[' && c != ']' && !c.is_whitespace())
|
||||
.is_ok_and(|&(_, c)| c != '#' && c != '[' && c != ']' && !c.is_whitespace())
|
||||
}) {
|
||||
match p {
|
||||
Ok(c) => r.push(c),
|
||||
Ok((_, c)) => r.push(c),
|
||||
Err(e) => return Some(Err(e)),
|
||||
}
|
||||
}
|
||||
|
|
@ -99,11 +231,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
struct Lexer<I: Iterator<Item = Result<char>>> {
|
||||
struct Lexer<I: Iterator<Item = Result<(usize, char)>>> {
|
||||
input: Peekable<Tokenizer<I>>,
|
||||
}
|
||||
|
||||
impl<I: Iterator<Item = Result<char>>> Lexer<I> {
|
||||
impl<I: Iterator<Item = Result<(usize, char)>>> Lexer<I> {
|
||||
fn new(iter: I) -> Self {
|
||||
Self {
|
||||
input: Tokenizer::new(iter).peekable(),
|
||||
|
|
@ -174,7 +306,7 @@ fn parse_look_at(iter: &mut impl Iterator<Item = Result<String>>) -> Result<Stat
|
|||
|
||||
fn parse_parameter<I, T>(iter: &mut Peekable<Tokenizer<I>>) -> Result<T>
|
||||
where
|
||||
I: Iterator<Item = Result<char>>,
|
||||
I: Iterator<Item = Result<(usize, char)>>,
|
||||
T: std::str::FromStr,
|
||||
<T as std::str::FromStr>::Err:
|
||||
std::marker::Send + std::marker::Sync + std::error::Error + 'static,
|
||||
|
|
@ -202,7 +334,7 @@ where
|
|||
|
||||
fn parse_list<I, T>(iter: &mut Peekable<Tokenizer<I>>, data: &mut Vec<T>) -> Result<()>
|
||||
where
|
||||
I: Iterator<Item = Result<char>>,
|
||||
I: Iterator<Item = Result<(usize, char)>>,
|
||||
T: std::str::FromStr,
|
||||
<T as std::str::FromStr>::Err:
|
||||
std::marker::Send + std::marker::Sync + std::error::Error + 'static,
|
||||
|
|
@ -237,7 +369,7 @@ fn parse_list_2<I, T, P, F>(
|
|||
f: F,
|
||||
) -> Result<()>
|
||||
where
|
||||
I: Iterator<Item = Result<char>>,
|
||||
I: Iterator<Item = Result<(usize, char)>>,
|
||||
T: std::str::FromStr,
|
||||
<T as std::str::FromStr>::Err:
|
||||
std::marker::Send + std::marker::Sync + std::error::Error + 'static,
|
||||
|
|
@ -283,7 +415,7 @@ fn parse_list_3<I, T, P, F>(
|
|||
f: F,
|
||||
) -> Result<()>
|
||||
where
|
||||
I: Iterator<Item = Result<char>>,
|
||||
I: Iterator<Item = Result<(usize, char)>>,
|
||||
T: std::str::FromStr,
|
||||
<T as std::str::FromStr>::Err:
|
||||
std::marker::Send + std::marker::Sync + std::error::Error + 'static,
|
||||
|
|
@ -331,7 +463,7 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_shape<I: Iterator<Item = Result<char>>>(
|
||||
fn parse_shape<I: Iterator<Item = Result<(usize, char)>>>(
|
||||
iter: &mut Peekable<Tokenizer<I>>,
|
||||
) -> std::result::Result<Statement, Error> {
|
||||
let shape_type = iter.next().ok_or(miette!("unable to get shape type"))??;
|
||||
|
|
@ -558,7 +690,7 @@ fn parse_shape<I: Iterator<Item = Result<char>>>(
|
|||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator<Item = Result<char>>> Lexer<I> {
|
||||
impl<I: Iterator<Item = Result<(usize, char)>>> Lexer<I> {
|
||||
fn next(&mut self, ctm: AffineTransform) -> Option<Result<Statement>> {
|
||||
match self.input.next() {
|
||||
Some(Ok(s)) => match s.as_str() {
|
||||
|
|
@ -613,7 +745,7 @@ impl<I: Iterator<Item = Result<char>>> Lexer<I> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_transform<I: Iterator<Item = Result<char>>>(
|
||||
fn parse_transform<I: Iterator<Item = Result<(usize, char)>>>(
|
||||
input: &mut Peekable<Tokenizer<I>>,
|
||||
) -> Result<AffineTransform> {
|
||||
if !input
|
||||
|
|
@ -652,7 +784,7 @@ fn parse_transform<I: Iterator<Item = Result<char>>>(
|
|||
.ok_or(miette!("Unable to invert transformation"))?)
|
||||
}
|
||||
|
||||
fn parse_translate<I: Iterator<Item = Result<char>>>(
|
||||
fn parse_translate<I: Iterator<Item = Result<(usize, char)>>>(
|
||||
iter: &mut Peekable<Tokenizer<I>>,
|
||||
) -> Result<Statement> {
|
||||
let pos = Pos3::new(
|
||||
|
|
@ -675,7 +807,7 @@ fn parse_translate<I: Iterator<Item = Result<char>>>(
|
|||
)))
|
||||
}
|
||||
|
||||
fn parse_scale<I: Iterator<Item = Result<char>>>(
|
||||
fn parse_scale<I: Iterator<Item = Result<(usize, char)>>>(
|
||||
iter: &mut Peekable<Tokenizer<I>>,
|
||||
) -> Result<Statement> {
|
||||
Ok(Statement::ConcatTransform(AffineTransform::scale(
|
||||
|
|
@ -694,7 +826,7 @@ fn parse_scale<I: Iterator<Item = Result<char>>>(
|
|||
)))
|
||||
}
|
||||
|
||||
fn parse_rotate<I: Iterator<Item = Result<char>>>(
|
||||
fn parse_rotate<I: Iterator<Item = Result<(usize, char)>>>(
|
||||
iter: &mut Peekable<Tokenizer<I>>,
|
||||
) -> Result<Statement> {
|
||||
let angle = iter
|
||||
|
|
@ -722,17 +854,25 @@ fn parse_rotate<I: Iterator<Item = Result<char>>>(
|
|||
)))
|
||||
}
|
||||
struct BytesToChar<I> {
|
||||
count: usize,
|
||||
iter: I,
|
||||
}
|
||||
|
||||
impl<I> BytesToChar<I> {
|
||||
fn new(iter: I) -> Self {
|
||||
Self { count: 0, iter }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator<Item = Result<u8, std::io::Error>>> Iterator for BytesToChar<I> {
|
||||
type Item = Result<char>;
|
||||
type Item = Result<(usize, char)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.iter.next()? {
|
||||
Ok(a) => {
|
||||
self.count += 1;
|
||||
if a & 0x80 == 0 {
|
||||
Some(Ok(char::from(a)))
|
||||
Some(Ok((self.count, char::from(a))))
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
|
|
@ -755,9 +895,7 @@ impl<P: AsRef<Path> + std::fmt::Debug> Parser<P> {
|
|||
Ok(Self {
|
||||
path,
|
||||
inner: None,
|
||||
iter: Lexer::new(BytesToChar {
|
||||
iter: bufreader.bytes(),
|
||||
}),
|
||||
iter: Lexer::new(BytesToChar::new(bufreader.bytes())),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue