Add more error handling
This commit is contained in:
		
							parent
							
								
									9065bfd7b4
								
							
						
					
					
						commit
						5b5e491cb2
					
				
					 4 changed files with 144 additions and 57 deletions
				
			
		|  | @ -34,5 +34,3 @@ AttributeBegin | ||||||
|       "point3 P" [ -20 -20 0   20 -20 0   -20 20 0   20 20 0 ] |       "point3 P" [ -20 -20 0   20 -20 0   -20 20 0   20 20 0 ] | ||||||
|       "point2 uv" [ 0 0   1 0    1 1   0 1 ] |       "point2 uv" [ 0 0   1 0    1 1   0 1 ] | ||||||
| AttributeEnd | AttributeEnd | ||||||
| 
 |  | ||||||
| " fd |  | ||||||
|  |  | ||||||
|  | @ -62,7 +62,7 @@ impl SourceCode for SourceFile { | ||||||
|                 } |                 } | ||||||
|             }) |             }) | ||||||
|             .map(|x| x + span.offset()) |             .map(|x| x + span.offset()) | ||||||
|             .unwrap_or(data[span.offset()..].len()); |             .unwrap_or(data.len()); | ||||||
| 
 | 
 | ||||||
|         let compact_data = &data[start_pos..end_pos]; |         let compact_data = &data[start_pos..end_pos]; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,18 +12,6 @@ use thiserror::Error; | ||||||
| mod tokenizer; | mod tokenizer; | ||||||
| 
 | 
 | ||||||
| mod error; | mod error; | ||||||
| 
 |  | ||||||
| #[derive(Error, Debug, Diagnostic)] |  | ||||||
| #[error("oops!")] |  | ||||||
| #[diagnostic(help("justus ist doof"))] |  | ||||||
| struct MyBad { |  | ||||||
|     #[source_code] |  | ||||||
|     src: SourceFile, |  | ||||||
| 
 |  | ||||||
|     #[label("Here")] |  | ||||||
|     bad_bit: SourceSpan, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct Lexer { | struct Lexer { | ||||||
|     input: Tokenizer, |     input: Tokenizer, | ||||||
| } | } | ||||||
|  | @ -40,6 +28,7 @@ impl Lexer { | ||||||
| enum Statement { | enum Statement { | ||||||
|     AttributeBegin, |     AttributeBegin, | ||||||
|     AttributeEnd, |     AttributeEnd, | ||||||
|  |     WorldBegin, | ||||||
|     Include(String), |     Include(String), | ||||||
|     ConcatTransform(AffineTransform), |     ConcatTransform(AffineTransform), | ||||||
|     Shape(ShapeType), |     Shape(ShapeType), | ||||||
|  | @ -295,6 +284,7 @@ impl Lexer { | ||||||
|                 "ConcatTransform" => { |                 "ConcatTransform" => { | ||||||
|                     Some(parse_transform(&mut self.input).map(Statement::ConcatTransform)) |                     Some(parse_transform(&mut self.input).map(Statement::ConcatTransform)) | ||||||
|                 } |                 } | ||||||
|  |                 "WorldBegin" => Some(Ok(Statement::WorldBegin)), | ||||||
|                 _ => { |                 _ => { | ||||||
|                     if s.chars().any(|c| !c.is_ascii_alphabetic()) { |                     if s.chars().any(|c| !c.is_ascii_alphabetic()) { | ||||||
|                         Some(Err(miette!("malformed identifier"))) |                         Some(Err(miette!("malformed identifier"))) | ||||||
|  | @ -359,20 +349,7 @@ fn parse_transform(input: &mut Tokenizer) -> Result<AffineTransform> { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn parse_translate(iter: &mut Tokenizer) -> Result<Statement> { | fn parse_translate(iter: &mut Tokenizer) -> Result<Statement> { | ||||||
|     let pos = Pos3::new( |     let pos = Pos3::new(iter.parse_next()?, iter.parse_next()?, iter.parse_next()?); | ||||||
|         iter.next() |  | ||||||
|             .ok_or(miette!("missing argument"))?? |  | ||||||
|             .parse() |  | ||||||
|             .into_diagnostic()?, |  | ||||||
|         iter.next() |  | ||||||
|             .ok_or(miette!("missing argument"))?? |  | ||||||
|             .parse() |  | ||||||
|             .into_diagnostic()?, |  | ||||||
|         iter.next() |  | ||||||
|             .ok_or(miette!("missing argument"))?? |  | ||||||
|             .parse() |  | ||||||
|             .into_diagnostic()?, |  | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     Ok(Statement::ConcatTransform(AffineTransform::translation( |     Ok(Statement::ConcatTransform(AffineTransform::translation( | ||||||
|         pos, |         pos, | ||||||
|  | @ -592,6 +569,7 @@ fn inner_parse_pbrt( | ||||||
|             Statement::Unknown(s, items) => { |             Statement::Unknown(s, items) => { | ||||||
|                 eprintln!("Unknown statement: {s}") |                 eprintln!("Unknown statement: {s}") | ||||||
|             } |             } | ||||||
|  |             Statement::WorldBegin => (), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ use std::{ | ||||||
| }; | }; | ||||||
| use thiserror::Error; | use thiserror::Error; | ||||||
| 
 | 
 | ||||||
| use crate::{BytesToChar, MyBad, SourceFile}; | use crate::{BytesToChar, SourceFile}; | ||||||
| 
 | 
 | ||||||
| pub struct Tokenizer { | pub struct Tokenizer { | ||||||
|     inner: InnerTokenizer, |     inner: InnerTokenizer, | ||||||
|  | @ -85,11 +85,11 @@ impl InnerTokenizer { | ||||||
|                     .next() |                     .next() | ||||||
|                     .is_none_or(|c| !c.is_ok_and(|(_, c)| c == '"')) |                     .is_none_or(|c| !c.is_ok_and(|(_, c)| c == '"')) | ||||||
|                 { |                 { | ||||||
|                     return Some(Err(From::from(MyBad { |                     return Some(Err(From::from(UnfinishedString { | ||||||
|                         src: SourceFile { |                         src: SourceFile { | ||||||
|                             path: PathBuf::from("ray-tracing-pbrt-scene/example.pbrt"), |                             path: PathBuf::from("ray-tracing-pbrt-scene/example.pbrt"), | ||||||
|                         }, |                         }, | ||||||
|                         bad_bit: SourceSpan::new(40.into(), 4), |                         bad_bit: SourceSpan::new(i.into(), r.len()), | ||||||
|                     }))); |                     }))); | ||||||
|                 }; |                 }; | ||||||
|                 r.push('"'); |                 r.push('"'); | ||||||
|  | @ -153,7 +153,12 @@ impl Tokenizer { | ||||||
|         <T as std::str::FromStr>::Err: |         <T as std::str::FromStr>::Err: | ||||||
|             std::error::Error + std::marker::Send + std::marker::Sync + 'static, |             std::error::Error + std::marker::Send + std::marker::Sync + 'static, | ||||||
|     { |     { | ||||||
|         let s = self.next().ok_or_else(|| miette!("Value expected"))??; |         let s = self.next().ok_or_else(|| MissingValueError { | ||||||
|  |             src: SourceFile { | ||||||
|  |                 path: self.path.to_path_buf(), | ||||||
|  |             }, | ||||||
|  |             bad_bit: self.last_span(), | ||||||
|  |         })??; | ||||||
| 
 | 
 | ||||||
|         s.parse::<T>().into_diagnostic().map_err(|e| { |         s.parse::<T>().into_diagnostic().map_err(|e| { | ||||||
|             ParsingError { |             ParsingError { | ||||||
|  | @ -167,6 +172,29 @@ impl Tokenizer { | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub fn parse_next_if<T>(&mut self, func: impl FnOnce(&String) -> bool) -> Option<Result<T>> | ||||||
|  |     where | ||||||
|  |         T: std::str::FromStr, | ||||||
|  |         <T as std::str::FromStr>::Err: | ||||||
|  |             std::error::Error + std::marker::Send + std::marker::Sync + 'static, | ||||||
|  |     { | ||||||
|  |         let s = self.next_if(func)?; | ||||||
|  | 
 | ||||||
|  |         Some(match s { | ||||||
|  |             Ok(s) => s.parse::<T>().into_diagnostic().map_err(|e| { | ||||||
|  |                 ParsingError { | ||||||
|  |                     src: SourceFile { | ||||||
|  |                         path: self.path.clone(), | ||||||
|  |                     }, | ||||||
|  |                     bad_bit: self.last_span, | ||||||
|  |                     error: Some(e), | ||||||
|  |                 } | ||||||
|  |                 .into() | ||||||
|  |             }), | ||||||
|  |             Err(e) => Err(e), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     pub fn last_span(&self) -> SourceSpan { |     pub fn last_span(&self) -> SourceSpan { | ||||||
|         self.last_span |         self.last_span | ||||||
|     } |     } | ||||||
|  | @ -177,7 +205,12 @@ impl Tokenizer { | ||||||
|         <T as std::str::FromStr>::Err: |         <T as std::str::FromStr>::Err: | ||||||
|             std::marker::Send + std::marker::Sync + std::error::Error + 'static, |             std::marker::Send + std::marker::Sync + std::error::Error + 'static, | ||||||
|     { |     { | ||||||
|         let p = self.next().ok_or(miette!("value expected"))??; |         let p = self.next().ok_or_else(|| ListBeginError { | ||||||
|  |             src: SourceFile { | ||||||
|  |                 path: self.path.to_path_buf(), | ||||||
|  |             }, | ||||||
|  |             bad_bit: self.last_span(), | ||||||
|  |         })??; | ||||||
| 
 | 
 | ||||||
|         match p.as_str() { |         match p.as_str() { | ||||||
|             "[" => { |             "[" => { | ||||||
|  | @ -186,7 +219,13 @@ impl Tokenizer { | ||||||
|                     .next() |                     .next() | ||||||
|                     .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]")) |                     .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]")) | ||||||
|                 { |                 { | ||||||
|                     bail!("expected closing bracket.") |                     return Err(ListEndError { | ||||||
|  |                         src: SourceFile { | ||||||
|  |                             path: self.path.to_path_buf(), | ||||||
|  |                         }, | ||||||
|  |                         bad_bit: self.last_span(), | ||||||
|  |                     } | ||||||
|  |                     .into()); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 Ok(d) |                 Ok(d) | ||||||
|  | @ -205,18 +244,30 @@ impl Tokenizer { | ||||||
|             .next() |             .next() | ||||||
|             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "[")) |             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "[")) | ||||||
|         { |         { | ||||||
|             bail!("expected list.") |             return Err(ListBeginError { | ||||||
|  |                 src: SourceFile { | ||||||
|  |                     path: self.path.to_path_buf(), | ||||||
|  |                 }, | ||||||
|  |                 bad_bit: self.last_span(), | ||||||
|  |             } | ||||||
|  |             .into()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         while let Some(p) = self.next_if(|p| p != "]").transpose()? { |         while let Some(p) = self.parse_next_if(|p| p != "]").transpose()? { | ||||||
|             data.push(p.parse().into_diagnostic()?); |             data.push(p); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if !self |         if !self | ||||||
|             .next() |             .next() | ||||||
|             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]")) |             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]")) | ||||||
|         { |         { | ||||||
|             bail!("expected list end.") |             return Err(ListEndError { | ||||||
|  |                 src: SourceFile { | ||||||
|  |                     path: self.path.to_path_buf(), | ||||||
|  |                 }, | ||||||
|  |                 bad_bit: self.last_span(), | ||||||
|  |             } | ||||||
|  |             .into()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Ok(()) |         Ok(()) | ||||||
|  | @ -233,15 +284,18 @@ impl Tokenizer { | ||||||
|             .next() |             .next() | ||||||
|             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "[")) |             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "[")) | ||||||
|         { |         { | ||||||
|             bail!("expected list.") |             return Err(ListBeginError { | ||||||
|  |                 src: SourceFile { | ||||||
|  |                     path: self.path.to_path_buf(), | ||||||
|  |                 }, | ||||||
|  |                 bad_bit: self.last_span(), | ||||||
|  |             } | ||||||
|  |             .into()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         while let Some(pa) = self.next_if(|p| p != "]").transpose()? { |         while let Some(pa) = self.parse_next_if(|p| p != "]").transpose()? { | ||||||
|             if let Some(pb) = self.next_if(|p| p != "]").transpose()? { |             if let Some(pb) = self.parse_next_if(|p| p != "]").transpose()? { | ||||||
|                 data.push(f( |                 data.push(f(pa, pb)); | ||||||
|                     pa.parse().into_diagnostic()?, |  | ||||||
|                     pb.parse().into_diagnostic()?, |  | ||||||
|                 )); |  | ||||||
|             } else { |             } else { | ||||||
|                 return Err(UncompleteError { |                 return Err(UncompleteError { | ||||||
|                     src: SourceFile { |                     src: SourceFile { | ||||||
|  | @ -257,7 +311,13 @@ impl Tokenizer { | ||||||
|             .next() |             .next() | ||||||
|             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]")) |             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]")) | ||||||
|         { |         { | ||||||
|             bail!("expected list end.") |             return Err(ListEndError { | ||||||
|  |                 src: SourceFile { | ||||||
|  |                     path: self.path.to_path_buf(), | ||||||
|  |                 }, | ||||||
|  |                 bad_bit: self.last_span(), | ||||||
|  |             } | ||||||
|  |             .into()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Ok(()) |         Ok(()) | ||||||
|  | @ -274,17 +334,19 @@ impl Tokenizer { | ||||||
|             .next() |             .next() | ||||||
|             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "[")) |             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "[")) | ||||||
|         { |         { | ||||||
|             bail!("expected list.") |             return Err(ListBeginError { | ||||||
|  |                 src: SourceFile { | ||||||
|  |                     path: self.path.to_path_buf(), | ||||||
|  |                 }, | ||||||
|  |                 bad_bit: self.last_span(), | ||||||
|  |             } | ||||||
|  |             .into()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         while let Some(pa) = self.next_if(|p| p != "]").transpose()? { |         while let Some(pa) = self.parse_next_if(|p| p != "]").transpose()? { | ||||||
|             if let Some(pb) = self.next_if(|p| p != "]").transpose()? { |             if let Some(pb) = self.parse_next_if(|p| p != "]").transpose()? { | ||||||
|                 if let Some(pc) = self.next_if(|p| p != "]").transpose()? { |                 if let Some(pc) = self.parse_next_if(|p| p != "]").transpose()? { | ||||||
|                     data.push(f( |                     data.push(f(pa, pb, pc)); | ||||||
|                         pa.parse().into_diagnostic()?, |  | ||||||
|                         pb.parse().into_diagnostic()?, |  | ||||||
|                         pc.parse().into_diagnostic()?, |  | ||||||
|                     )); |  | ||||||
|                 } else { |                 } else { | ||||||
|                     return Err(UncompleteError { |                     return Err(UncompleteError { | ||||||
|                         src: SourceFile { |                         src: SourceFile { | ||||||
|  | @ -309,7 +371,13 @@ impl Tokenizer { | ||||||
|             .next() |             .next() | ||||||
|             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]")) |             .is_none_or(|p| p.is_ok_and(|p| p.as_str() == "]")) | ||||||
|         { |         { | ||||||
|             bail!("expected list end.") |             return Err(ListEndError { | ||||||
|  |                 src: SourceFile { | ||||||
|  |                     path: self.path.to_path_buf(), | ||||||
|  |                 }, | ||||||
|  |                 bad_bit: self.last_span(), | ||||||
|  |             } | ||||||
|  |             .into()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Ok(()) |         Ok(()) | ||||||
|  | @ -340,3 +408,46 @@ struct UncompleteError { | ||||||
|     #[label("Here")] |     #[label("Here")] | ||||||
|     bad_bit: SourceSpan, |     bad_bit: SourceSpan, | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[derive(Error, Debug, Diagnostic)] | ||||||
|  | #[error("Unfinished string")] | ||||||
|  | struct UnfinishedString { | ||||||
|  |     #[source_code] | ||||||
|  |     src: SourceFile, | ||||||
|  | 
 | ||||||
|  |     #[label("Here")] | ||||||
|  |     bad_bit: SourceSpan, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Error, Debug, Diagnostic)] | ||||||
|  | #[error("list error")] | ||||||
|  | #[diagnostic(help("expected list"))] | ||||||
|  | struct ListBeginError { | ||||||
|  |     #[source_code] | ||||||
|  |     src: SourceFile, | ||||||
|  | 
 | ||||||
|  |     #[label("Here")] | ||||||
|  |     bad_bit: SourceSpan, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Error, Debug, Diagnostic)] | ||||||
|  | #[error("list error")] | ||||||
|  | #[diagnostic(help("expected list to end"))] | ||||||
|  | struct ListEndError { | ||||||
|  |     #[source_code] | ||||||
|  |     src: SourceFile, | ||||||
|  | 
 | ||||||
|  |     #[label("Here")] | ||||||
|  |     bad_bit: SourceSpan, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Error, Debug, Diagnostic)] | ||||||
|  | #[error("value expected")] | ||||||
|  | #[diagnostic(help("expected a value"))] | ||||||
|  | struct MissingValueError { | ||||||
|  |     #[source_code] | ||||||
|  |     src: SourceFile, | ||||||
|  | 
 | ||||||
|  |     #[label("Here")] | ||||||
|  |     bad_bit: SourceSpan, | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue