Improve and use macro for parsing dictionaries
This commit is contained in:
parent
d6662da7b9
commit
63a210b8b4
3 changed files with 202 additions and 226 deletions
|
|
@ -12,7 +12,7 @@ fn main() -> Result<(), miette::Error> {
|
|||
|
||||
let t = parse_pbrt_v4(args.filename)?;
|
||||
|
||||
// dbg!(t);
|
||||
dbg!(t);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use ray_tracing_core::{
|
|||
use std::path::{Path, PathBuf};
|
||||
use thiserror::Error;
|
||||
|
||||
#[macro_use]
|
||||
mod tokenizer;
|
||||
|
||||
mod error;
|
||||
|
|
@ -47,47 +48,6 @@ fn parse_look_at(iter: &mut Tokenizer) -> Result<Statement> {
|
|||
))
|
||||
}
|
||||
|
||||
macro_rules! parse_dict {
|
||||
($tokenizer:expr => $($name_decl:ident, $type:ty, $default:expr);+ => $($name_parsing:ident, $expr:expr, $parsing:expr);+
|
||||
) => {
|
||||
{
|
||||
$(
|
||||
let mut $name_decl = None;
|
||||
)+
|
||||
|
||||
while let Some(p) = $tokenizer.next_if(|p| p.starts_with('"')).transpose()? {
|
||||
match p.as_str() {
|
||||
$(
|
||||
$expr => {
|
||||
if $name_parsing.is_none() {
|
||||
$name_parsing = Some($parsing);
|
||||
} else {
|
||||
return Err(miette!("dfs"))
|
||||
}
|
||||
}
|
||||
)+
|
||||
_ => {todo!()}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Dict {
|
||||
$(
|
||||
$name_decl: $type,
|
||||
)+
|
||||
}
|
||||
|
||||
$(
|
||||
let $name_decl = $name_decl.unwrap_or_else(|| $default);
|
||||
)*
|
||||
|
||||
Dict {
|
||||
$($name_decl,)*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn parse_shape(iter: &mut Tokenizer) -> Result<Statement> {
|
||||
let shape_type = iter.next().ok_or(miette!("unable to get shape type"))??;
|
||||
|
||||
|
|
@ -118,242 +78,185 @@ fn parse_shape(iter: &mut Tokenizer) -> Result<Statement> {
|
|||
))
|
||||
}
|
||||
"\"trianglemesh\"" => {
|
||||
let mut indices = Vec::new();
|
||||
let mut p = Vec::new();
|
||||
let mut n = Vec::new();
|
||||
let mut s = Vec::new();
|
||||
let mut uv = Vec::new();
|
||||
let mut alpha = ShapeAlpha::None;
|
||||
let t = parse_dict!(iter =>
|
||||
p, Vec<Pos3>, Vec::new();
|
||||
n, Vec<Dir3>, Vec::new();
|
||||
s, Vec<Dir3>, Vec::new();
|
||||
uv, Vec<[Float; 2]>, Vec::new();
|
||||
indices, Vec<usize>, Vec::new();
|
||||
alpha, ShapeAlpha, ShapeAlpha::None
|
||||
=>
|
||||
p, "\"point3 P\"", iter.parse_list_3(Pos3::new)?;
|
||||
n, "\"normal N\"", iter.parse_list_3(Dir3::new)?;
|
||||
s, "\"normal S\"", iter.parse_list_3(Dir3::new)?;
|
||||
uv, "\"point2 uv\"", iter.parse_list_2(|u, v| [u, v])?;
|
||||
indices, "\"interger indices\"", iter.parse_list()?;
|
||||
alpha, "\"float alpha\"", ShapeAlpha::Value(iter.parse_parameter()?);
|
||||
alpha, "\"texture alpha\"", ShapeAlpha::Texture(iter.parse_parameter()?)
|
||||
);
|
||||
|
||||
while let Some(q) = iter.next_if(|p| p.starts_with('"')).transpose()? {
|
||||
match q.as_str() {
|
||||
"\"integer indices\"" => {
|
||||
iter.parse_list(&mut indices)?;
|
||||
}
|
||||
"\"point3 P\"" => {
|
||||
iter.parse_list_3(&mut p, Pos3::new)?;
|
||||
}
|
||||
"\"normal N\"" => {
|
||||
iter.parse_list_3(&mut n, Dir3::new)?;
|
||||
}
|
||||
"\"vector S\"" => {
|
||||
iter.parse_list_3(&mut s, Dir3::new)?;
|
||||
}
|
||||
"\"point2 uv\"" => {
|
||||
iter.parse_list_2(&mut uv, |u, v| [u, v])?;
|
||||
}
|
||||
"\"float alpha\"" => {
|
||||
alpha = ShapeAlpha::Value(iter.parse_parameter()?);
|
||||
}
|
||||
"\"texture alpha\"" => {
|
||||
alpha = ShapeAlpha::Texture(iter.parse_parameter()?);
|
||||
}
|
||||
_ => {
|
||||
bail!("unknown argument {}", q)
|
||||
}
|
||||
}
|
||||
}
|
||||
if p.len() < 3 {
|
||||
if t.p.len() < 3 {
|
||||
bail!("At least 3 points required.")
|
||||
}
|
||||
if indices.is_empty() && p.len() != 3 {
|
||||
if t.indices.is_empty() && t.p.len() != 3 {
|
||||
bail!("Indices required for trianglemesh with more than 3 points.")
|
||||
}
|
||||
|
||||
if indices.len() % 3 != 0 {
|
||||
if t.indices.len() % 3 != 0 {
|
||||
bail!(
|
||||
"number of indices must be divisible by 3. num indices: {}",
|
||||
indices.len()
|
||||
t.indices.len()
|
||||
)
|
||||
}
|
||||
|
||||
if !n.is_empty() && n.len() != p.len() {
|
||||
if !t.n.is_empty() && t.n.len() != t.p.len() {
|
||||
bail!("Number of normals not equal to number of positions.")
|
||||
}
|
||||
|
||||
if !s.is_empty() && s.len() != p.len() {
|
||||
if !t.s.is_empty() && t.s.len() != t.p.len() {
|
||||
bail!("Number of tangents not equal to number of positions.")
|
||||
}
|
||||
if !uv.is_empty() && uv.len() != p.len() {
|
||||
if !t.uv.is_empty() && t.uv.len() != t.p.len() {
|
||||
bail!("Number of uvs not equal to number of positions.")
|
||||
}
|
||||
|
||||
Ok(Statement::Shape(
|
||||
ShapeType::TriangleMesh {
|
||||
indices,
|
||||
p,
|
||||
n,
|
||||
s,
|
||||
uv,
|
||||
indices: t.indices,
|
||||
p: t.p,
|
||||
n: t.n,
|
||||
s: t.s,
|
||||
uv: t.uv,
|
||||
},
|
||||
alpha,
|
||||
t.alpha,
|
||||
))
|
||||
}
|
||||
"\"bilinearmesh\"" => {
|
||||
let mut indices = Vec::new();
|
||||
let mut p = Vec::new();
|
||||
let mut n = Vec::new();
|
||||
let mut uv = Vec::new();
|
||||
let mut alpha = ShapeAlpha::None;
|
||||
let t = parse_dict!(iter =>
|
||||
p, Vec<Pos3>, Vec::new();
|
||||
n, Vec<Dir3>, Vec::new();
|
||||
uv, Vec<[Float; 2]>, Vec::new();
|
||||
indices, Vec<usize>, Vec::new();
|
||||
alpha, ShapeAlpha, ShapeAlpha::None
|
||||
=>
|
||||
p, "\"point3 P\"", iter.parse_list_3(Pos3::new)?;
|
||||
n, "\"normal N\"", iter.parse_list_3(Dir3::new)?;
|
||||
uv, "\"point2 uv\"", iter.parse_list_2(|u, v| [u, v])?;
|
||||
indices, "\"interger indices\"", iter.parse_list()?;
|
||||
alpha, "\"float alpha\"", ShapeAlpha::Value(iter.parse_parameter()?);
|
||||
alpha, "\"texture alpha\"", ShapeAlpha::Texture(iter.parse_parameter()?)
|
||||
);
|
||||
|
||||
while let Some(q) = iter.next_if(|p| p.starts_with('"')).transpose()? {
|
||||
match q.as_str() {
|
||||
"\"integer indices\"" => {
|
||||
iter.parse_list(&mut indices)?;
|
||||
}
|
||||
"\"point3 P\"" => {
|
||||
iter.parse_list_3(&mut p, Pos3::new)?;
|
||||
}
|
||||
"\"point2 uv\"" => {
|
||||
iter.parse_list_2(&mut uv, |u, v| [u, v])?;
|
||||
}
|
||||
"\"vector N\"" => {
|
||||
iter.parse_list_3(&mut n, Dir3::new)?;
|
||||
}
|
||||
"\"float alpha\"" => {
|
||||
alpha = ShapeAlpha::Value(iter.parse_parameter()?);
|
||||
}
|
||||
"\"texture alpha\"" => {
|
||||
alpha = ShapeAlpha::Texture(iter.parse_parameter()?);
|
||||
}
|
||||
_ => {
|
||||
bail!("unknown argument {}", q)
|
||||
}
|
||||
}
|
||||
if t.p.len() < 4 {
|
||||
bail!("At least 4 points required.")
|
||||
}
|
||||
if t.indices.is_empty() && t.p.len() != 4 {
|
||||
bail!("Indices required for trianglemesh with more than 4 points.")
|
||||
}
|
||||
|
||||
if p.len() < 4 {
|
||||
bail!("At least 3 points required.")
|
||||
}
|
||||
if indices.is_empty() && p.len() != 4 {
|
||||
bail!("Indices required for trianglemesh with more than 3 points.")
|
||||
}
|
||||
|
||||
if indices.len() % 4 != 0 {
|
||||
if t.indices.len() % 4 != 0 {
|
||||
bail!(
|
||||
"number of indices must be divisible by 4. num indices: {}",
|
||||
indices.len()
|
||||
t.indices.len()
|
||||
)
|
||||
}
|
||||
|
||||
if !n.is_empty() && n.len() != p.len() {
|
||||
if !t.n.is_empty() && t.n.len() != t.p.len() {
|
||||
bail!("Number of normals not equal to number of positions.")
|
||||
}
|
||||
|
||||
if !uv.is_empty() && uv.len() != p.len() {
|
||||
if !t.uv.is_empty() && t.uv.len() != t.p.len() {
|
||||
bail!("Number of uvs not equal to number of positions.")
|
||||
}
|
||||
|
||||
Ok(Statement::Shape(
|
||||
ShapeType::BilinearMesh { indices, p, n, uv },
|
||||
alpha,
|
||||
ShapeType::BilinearMesh {
|
||||
indices: t.indices,
|
||||
p: t.p,
|
||||
n: t.n,
|
||||
uv: t.uv,
|
||||
},
|
||||
t.alpha,
|
||||
))
|
||||
}
|
||||
|
||||
"\"loopsubdiv\"" => {
|
||||
let mut levels = 3;
|
||||
let mut indices = Vec::new();
|
||||
let t = parse_dict!(iter =>
|
||||
levels, u32, 3;
|
||||
indices, Vec<usize>, Vec::new();
|
||||
p, Vec<Pos3>, Vec::new();
|
||||
alpha, ShapeAlpha, ShapeAlpha::None
|
||||
=>
|
||||
levels, "\"float levels\"", iter.parse_parameter()?;
|
||||
indices, "\"interger indices\"", iter.parse_list()?;
|
||||
p, "\"point3 P\"", iter.parse_list_3(Pos3::new)?;
|
||||
alpha, "\"float alpha\"", ShapeAlpha::Value(iter.parse_parameter()?);
|
||||
alpha, "\"texture alpha\"", ShapeAlpha::Texture(iter.parse_parameter()?)
|
||||
);
|
||||
|
||||
let mut p = Vec::new();
|
||||
let mut alpha = ShapeAlpha::None;
|
||||
|
||||
while let Some(q) = iter.next_if(|p| p.starts_with('"')).transpose()? {
|
||||
match q.as_str() {
|
||||
"\"point3 P\"" => {
|
||||
iter.parse_list_3(&mut p, Pos3::new)?;
|
||||
}
|
||||
"\"integer indices\"" => {
|
||||
iter.parse_list(&mut indices)?;
|
||||
}
|
||||
"\"integer levels\"" => {
|
||||
levels = iter.parse_parameter()?;
|
||||
}
|
||||
"\"float alpha\"" => {
|
||||
alpha = ShapeAlpha::Value(iter.parse_parameter()?);
|
||||
}
|
||||
"\"texture alpha\"" => {
|
||||
alpha = ShapeAlpha::Texture(iter.parse_parameter()?);
|
||||
}
|
||||
_ => {
|
||||
bail!("unknown argument {}", q)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if indices.is_empty() {
|
||||
if t.indices.is_empty() {
|
||||
bail!("indices are a required field")
|
||||
}
|
||||
|
||||
if p.is_empty() {
|
||||
if t.p.is_empty() {
|
||||
bail!("p is a required field")
|
||||
}
|
||||
|
||||
Ok(Statement::Shape(
|
||||
ShapeType::LoopSubDiv { levels, indices, p },
|
||||
alpha,
|
||||
ShapeType::LoopSubDiv {
|
||||
levels: t.levels,
|
||||
indices: t.indices,
|
||||
p: t.p,
|
||||
},
|
||||
t.alpha,
|
||||
))
|
||||
}
|
||||
"\"disk\"" => {
|
||||
let mut height = 0.0;
|
||||
let mut radius = 1.0;
|
||||
let mut innerradius = 0.0;
|
||||
let mut phimax = 360.0;
|
||||
let mut alpha = ShapeAlpha::None;
|
||||
|
||||
while let Some(q) = iter.next_if(|p| p.starts_with('"')).transpose()? {
|
||||
match q.as_str() {
|
||||
"\"float height\"" => height = iter.parse_parameter()?,
|
||||
"\"float radius\"" => radius = iter.parse_parameter()?,
|
||||
"\"float innerradius\"" => innerradius = iter.parse_parameter()?,
|
||||
"\"float phimax\"" => phimax = iter.parse_parameter()?,
|
||||
"\"float alpha\"" => {
|
||||
alpha = ShapeAlpha::Value(iter.parse_parameter()?);
|
||||
}
|
||||
"\"texture alpha\"" => {
|
||||
alpha = ShapeAlpha::Texture(iter.parse_parameter()?);
|
||||
}
|
||||
_ => {
|
||||
bail!("unknown argument {}", q)
|
||||
}
|
||||
}
|
||||
}
|
||||
let t = parse_dict!(iter =>
|
||||
height, Float, 0.0;
|
||||
radius, Float, 1.0;
|
||||
innerradius, Float, 0.0;
|
||||
phimax, Float, 360.0;
|
||||
alpha, ShapeAlpha, ShapeAlpha::None
|
||||
=>
|
||||
height, "\"float height\"", iter.parse_parameter()?;
|
||||
radius, "\"float radius\"", iter.parse_parameter()?;
|
||||
innerradius, "\"float innerradius\"", iter.parse_parameter()?;
|
||||
phimax, "\"float phimax\"", iter.parse_parameter()?;
|
||||
alpha, "\"float alpha\"", ShapeAlpha::Value(iter.parse_parameter()?);
|
||||
alpha, "\"texture alpha\"", ShapeAlpha::Texture(iter.parse_parameter()?)
|
||||
);
|
||||
|
||||
Ok(Statement::Shape(
|
||||
ShapeType::Disk {
|
||||
height,
|
||||
radius,
|
||||
innerradius,
|
||||
phimax,
|
||||
height: t.height,
|
||||
radius: t.radius,
|
||||
innerradius: t.innerradius,
|
||||
phimax: t.phimax,
|
||||
},
|
||||
alpha,
|
||||
t.alpha,
|
||||
))
|
||||
}
|
||||
"\"plymesh\"" => {
|
||||
let mut filename = String::new();
|
||||
let mut displacement = None;
|
||||
let mut edgelength = 1.0;
|
||||
let mut alpha = ShapeAlpha::None;
|
||||
while let Some(q) = iter.next_if(|p| p.starts_with('"')).transpose()? {
|
||||
match q.as_str() {
|
||||
"\"string filename\"" => filename = dbg!(iter.parse_parameter()?),
|
||||
"\"texture displacement\"" => displacement = Some(iter.parse_parameter()?),
|
||||
"\"float edgelength\"" => edgelength = iter.parse_parameter()?,
|
||||
"\"float alpha\"" => {
|
||||
alpha = ShapeAlpha::Value(iter.parse_parameter()?);
|
||||
}
|
||||
"\"texture alpha\"" => {
|
||||
alpha = ShapeAlpha::Texture(iter.parse_parameter()?);
|
||||
}
|
||||
_ => {
|
||||
bail!("unknown argument {}", q)
|
||||
}
|
||||
}
|
||||
}
|
||||
let t = parse_dict!(iter =>
|
||||
filename, String, String::new();
|
||||
displacement, Option<String>, None;
|
||||
edgelength, Float, 1.0;
|
||||
alpha, ShapeAlpha, ShapeAlpha::None
|
||||
=>
|
||||
filename, "\"string filename\"", iter.parse_next()?;
|
||||
displacement, "\"string displacement\"", Some(iter.parse_next()?);
|
||||
edgelength, "\"float edgelength\"", iter.parse_parameter()?;
|
||||
alpha, "\"float alpha\"", ShapeAlpha::Value(iter.parse_parameter()?);
|
||||
alpha, "\"texture alpha\"", ShapeAlpha::Texture(iter.parse_parameter()?)
|
||||
);
|
||||
|
||||
Ok(Statement::Shape(
|
||||
ShapeType::PlyMesh {
|
||||
filename,
|
||||
displacement,
|
||||
edgelength,
|
||||
filename: t.filename,
|
||||
displacement: t.displacement,
|
||||
edgelength: t.edgelength,
|
||||
},
|
||||
alpha,
|
||||
t.alpha,
|
||||
))
|
||||
}
|
||||
_ => Err(miette!("Unknown shape {}", shape_type)),
|
||||
|
|
@ -586,10 +489,10 @@ enum ShapeType {
|
|||
p: Vec<Pos3>,
|
||||
},
|
||||
Disk {
|
||||
height: f64,
|
||||
radius: f64,
|
||||
innerradius: f64,
|
||||
phimax: f64,
|
||||
height: Float,
|
||||
radius: Float,
|
||||
innerradius: Float,
|
||||
phimax: Float,
|
||||
},
|
||||
PlyMesh {
|
||||
filename: String,
|
||||
|
|
|
|||
|
|
@ -234,12 +234,13 @@ impl Tokenizer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_list<T>(&mut self, data: &mut Vec<T>) -> Result<()>
|
||||
pub fn parse_list<T>(&mut self) -> Result<Vec<T>>
|
||||
where
|
||||
T: std::str::FromStr,
|
||||
<T as std::str::FromStr>::Err:
|
||||
std::marker::Send + std::marker::Sync + std::error::Error + 'static,
|
||||
{
|
||||
let mut data = Vec::new();
|
||||
if !self
|
||||
.next()
|
||||
.is_none_or(|p| p.is_ok_and(|p| p.as_str() == "["))
|
||||
|
|
@ -270,16 +271,17 @@ impl Tokenizer {
|
|||
.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn parse_list_2<T, P, F>(&mut self, data: &mut Vec<P>, f: F) -> Result<()>
|
||||
pub fn parse_list_2<T, P, F>(&mut self, f: F) -> Result<Vec<P>>
|
||||
where
|
||||
T: std::str::FromStr,
|
||||
<T as std::str::FromStr>::Err:
|
||||
std::marker::Send + std::marker::Sync + std::error::Error + 'static,
|
||||
F: Fn(T, T) -> P,
|
||||
{
|
||||
let mut data = Vec::new();
|
||||
if !self
|
||||
.next()
|
||||
.is_none_or(|p| p.is_ok_and(|p| p.as_str() == "["))
|
||||
|
|
@ -320,16 +322,17 @@ impl Tokenizer {
|
|||
.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn parse_list_3<T, P, F>(&mut self, data: &mut Vec<P>, f: F) -> Result<()>
|
||||
pub fn parse_list_3<T, P, F>(&mut self, f: F) -> Result<Vec<P>>
|
||||
where
|
||||
T: std::str::FromStr,
|
||||
<T as std::str::FromStr>::Err:
|
||||
std::marker::Send + std::marker::Sync + std::error::Error + 'static,
|
||||
F: Fn(T, T, T) -> P,
|
||||
{
|
||||
let mut data = Vec::new();
|
||||
if !self
|
||||
.next()
|
||||
.is_none_or(|p| p.is_ok_and(|p| p.as_str() == "["))
|
||||
|
|
@ -380,8 +383,78 @@ impl Tokenizer {
|
|||
.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn get_src(&self) -> SourceFile {
|
||||
SourceFile {
|
||||
path: self.path.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! parse_dict {
|
||||
($tokenizer:expr => $($name_decl:ident, $type:ty, $default:expr);+ => $($name_parsing:ident, $expr:expr, $parsing:expr);+
|
||||
) => {
|
||||
{
|
||||
$(
|
||||
let mut $name_decl = None;
|
||||
)+
|
||||
|
||||
while let Some(p) = $tokenizer.next_if(|p| p.starts_with('"')).transpose()? {
|
||||
match p.as_str() {
|
||||
$(
|
||||
$expr => {
|
||||
if $name_parsing.is_none() {
|
||||
$name_parsing = Some($parsing);
|
||||
} else {
|
||||
return Err($crate::tokenizer::DuplicateDictEntryError {bad_bit: $tokenizer.last_span(), src: $tokenizer.get_src()}.into())
|
||||
}
|
||||
}
|
||||
)+
|
||||
_ => {
|
||||
return Err($crate::tokenizer::UnknownDictEntryError {bad_bit: $tokenizer.last_span(), src: $tokenizer.get_src()}.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Dict {
|
||||
$(
|
||||
$name_decl: $type,
|
||||
)+
|
||||
}
|
||||
|
||||
$(
|
||||
let $name_decl = $name_decl.unwrap_or_else(|| $default);
|
||||
)*
|
||||
|
||||
Dict {
|
||||
$($name_decl,)*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, Diagnostic)]
|
||||
#[error("Duplicate dict entry error")]
|
||||
#[diagnostic(help("multiple dict entries with the same key"))]
|
||||
pub struct DuplicateDictEntryError {
|
||||
#[source_code]
|
||||
pub src: SourceFile,
|
||||
|
||||
#[label("Here")]
|
||||
pub bad_bit: SourceSpan,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, Diagnostic)]
|
||||
#[error("Unknown dict entry error")]
|
||||
pub struct UnknownDictEntryError {
|
||||
#[source_code]
|
||||
pub src: SourceFile,
|
||||
|
||||
#[label("Here")]
|
||||
pub bad_bit: SourceSpan,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, Diagnostic)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue