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)?;
|
let t = parse_pbrt_v4(args.filename)?;
|
||||||
|
|
||||||
// dbg!(t);
|
dbg!(t);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use ray_tracing_core::{
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
mod tokenizer;
|
mod tokenizer;
|
||||||
|
|
||||||
mod error;
|
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> {
|
fn parse_shape(iter: &mut Tokenizer) -> Result<Statement> {
|
||||||
let shape_type = iter.next().ok_or(miette!("unable to get shape type"))??;
|
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\"" => {
|
"\"trianglemesh\"" => {
|
||||||
let mut indices = Vec::new();
|
let t = parse_dict!(iter =>
|
||||||
let mut p = Vec::new();
|
p, Vec<Pos3>, Vec::new();
|
||||||
let mut n = Vec::new();
|
n, Vec<Dir3>, Vec::new();
|
||||||
let mut s = Vec::new();
|
s, Vec<Dir3>, Vec::new();
|
||||||
let mut uv = Vec::new();
|
uv, Vec<[Float; 2]>, Vec::new();
|
||||||
let mut alpha = ShapeAlpha::None;
|
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()? {
|
if t.p.len() < 3 {
|
||||||
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 {
|
|
||||||
bail!("At least 3 points required.")
|
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.")
|
bail!("Indices required for trianglemesh with more than 3 points.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if indices.len() % 3 != 0 {
|
if t.indices.len() % 3 != 0 {
|
||||||
bail!(
|
bail!(
|
||||||
"number of indices must be divisible by 3. num indices: {}",
|
"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.")
|
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.")
|
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.")
|
bail!("Number of uvs not equal to number of positions.")
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Statement::Shape(
|
Ok(Statement::Shape(
|
||||||
ShapeType::TriangleMesh {
|
ShapeType::TriangleMesh {
|
||||||
indices,
|
indices: t.indices,
|
||||||
p,
|
p: t.p,
|
||||||
n,
|
n: t.n,
|
||||||
s,
|
s: t.s,
|
||||||
uv,
|
uv: t.uv,
|
||||||
},
|
},
|
||||||
alpha,
|
t.alpha,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
"\"bilinearmesh\"" => {
|
"\"bilinearmesh\"" => {
|
||||||
let mut indices = Vec::new();
|
let t = parse_dict!(iter =>
|
||||||
let mut p = Vec::new();
|
p, Vec<Pos3>, Vec::new();
|
||||||
let mut n = Vec::new();
|
n, Vec<Dir3>, Vec::new();
|
||||||
let mut uv = Vec::new();
|
uv, Vec<[Float; 2]>, Vec::new();
|
||||||
let mut alpha = ShapeAlpha::None;
|
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()? {
|
if t.p.len() < 4 {
|
||||||
match q.as_str() {
|
bail!("At least 4 points required.")
|
||||||
"\"integer indices\"" => {
|
}
|
||||||
iter.parse_list(&mut indices)?;
|
if t.indices.is_empty() && t.p.len() != 4 {
|
||||||
}
|
bail!("Indices required for trianglemesh with more than 4 points.")
|
||||||
"\"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 p.len() < 4 {
|
if t.indices.len() % 4 != 0 {
|
||||||
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 {
|
|
||||||
bail!(
|
bail!(
|
||||||
"number of indices must be divisible by 4. num indices: {}",
|
"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.")
|
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.")
|
bail!("Number of uvs not equal to number of positions.")
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Statement::Shape(
|
Ok(Statement::Shape(
|
||||||
ShapeType::BilinearMesh { indices, p, n, uv },
|
ShapeType::BilinearMesh {
|
||||||
alpha,
|
indices: t.indices,
|
||||||
|
p: t.p,
|
||||||
|
n: t.n,
|
||||||
|
uv: t.uv,
|
||||||
|
},
|
||||||
|
t.alpha,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
"\"loopsubdiv\"" => {
|
"\"loopsubdiv\"" => {
|
||||||
let mut levels = 3;
|
let t = parse_dict!(iter =>
|
||||||
let mut indices = Vec::new();
|
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();
|
if t.indices.is_empty() {
|
||||||
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() {
|
|
||||||
bail!("indices are a required field")
|
bail!("indices are a required field")
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.is_empty() {
|
if t.p.is_empty() {
|
||||||
bail!("p is a required field")
|
bail!("p is a required field")
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Statement::Shape(
|
Ok(Statement::Shape(
|
||||||
ShapeType::LoopSubDiv { levels, indices, p },
|
ShapeType::LoopSubDiv {
|
||||||
alpha,
|
levels: t.levels,
|
||||||
|
indices: t.indices,
|
||||||
|
p: t.p,
|
||||||
|
},
|
||||||
|
t.alpha,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
"\"disk\"" => {
|
"\"disk\"" => {
|
||||||
let mut height = 0.0;
|
let t = parse_dict!(iter =>
|
||||||
let mut radius = 1.0;
|
height, Float, 0.0;
|
||||||
let mut innerradius = 0.0;
|
radius, Float, 1.0;
|
||||||
let mut phimax = 360.0;
|
innerradius, Float, 0.0;
|
||||||
let mut alpha = ShapeAlpha::None;
|
phimax, Float, 360.0;
|
||||||
|
alpha, ShapeAlpha, ShapeAlpha::None
|
||||||
while let Some(q) = iter.next_if(|p| p.starts_with('"')).transpose()? {
|
=>
|
||||||
match q.as_str() {
|
height, "\"float height\"", iter.parse_parameter()?;
|
||||||
"\"float height\"" => height = iter.parse_parameter()?,
|
radius, "\"float radius\"", iter.parse_parameter()?;
|
||||||
"\"float radius\"" => radius = iter.parse_parameter()?,
|
innerradius, "\"float innerradius\"", iter.parse_parameter()?;
|
||||||
"\"float innerradius\"" => innerradius = iter.parse_parameter()?,
|
phimax, "\"float phimax\"", iter.parse_parameter()?;
|
||||||
"\"float phimax\"" => phimax = iter.parse_parameter()?,
|
alpha, "\"float alpha\"", ShapeAlpha::Value(iter.parse_parameter()?);
|
||||||
"\"float alpha\"" => {
|
alpha, "\"texture alpha\"", ShapeAlpha::Texture(iter.parse_parameter()?)
|
||||||
alpha = ShapeAlpha::Value(iter.parse_parameter()?);
|
);
|
||||||
}
|
|
||||||
"\"texture alpha\"" => {
|
|
||||||
alpha = ShapeAlpha::Texture(iter.parse_parameter()?);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
bail!("unknown argument {}", q)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Statement::Shape(
|
Ok(Statement::Shape(
|
||||||
ShapeType::Disk {
|
ShapeType::Disk {
|
||||||
height,
|
height: t.height,
|
||||||
radius,
|
radius: t.radius,
|
||||||
innerradius,
|
innerradius: t.innerradius,
|
||||||
phimax,
|
phimax: t.phimax,
|
||||||
},
|
},
|
||||||
alpha,
|
t.alpha,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
"\"plymesh\"" => {
|
"\"plymesh\"" => {
|
||||||
let mut filename = String::new();
|
let t = parse_dict!(iter =>
|
||||||
let mut displacement = None;
|
filename, String, String::new();
|
||||||
let mut edgelength = 1.0;
|
displacement, Option<String>, None;
|
||||||
let mut alpha = ShapeAlpha::None;
|
edgelength, Float, 1.0;
|
||||||
while let Some(q) = iter.next_if(|p| p.starts_with('"')).transpose()? {
|
alpha, ShapeAlpha, ShapeAlpha::None
|
||||||
match q.as_str() {
|
=>
|
||||||
"\"string filename\"" => filename = dbg!(iter.parse_parameter()?),
|
filename, "\"string filename\"", iter.parse_next()?;
|
||||||
"\"texture displacement\"" => displacement = Some(iter.parse_parameter()?),
|
displacement, "\"string displacement\"", Some(iter.parse_next()?);
|
||||||
"\"float edgelength\"" => edgelength = iter.parse_parameter()?,
|
edgelength, "\"float edgelength\"", iter.parse_parameter()?;
|
||||||
"\"float alpha\"" => {
|
alpha, "\"float alpha\"", ShapeAlpha::Value(iter.parse_parameter()?);
|
||||||
alpha = ShapeAlpha::Value(iter.parse_parameter()?);
|
alpha, "\"texture alpha\"", ShapeAlpha::Texture(iter.parse_parameter()?)
|
||||||
}
|
);
|
||||||
"\"texture alpha\"" => {
|
|
||||||
alpha = ShapeAlpha::Texture(iter.parse_parameter()?);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
bail!("unknown argument {}", q)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Statement::Shape(
|
Ok(Statement::Shape(
|
||||||
ShapeType::PlyMesh {
|
ShapeType::PlyMesh {
|
||||||
filename,
|
filename: t.filename,
|
||||||
displacement,
|
displacement: t.displacement,
|
||||||
edgelength,
|
edgelength: t.edgelength,
|
||||||
},
|
},
|
||||||
alpha,
|
t.alpha,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
_ => Err(miette!("Unknown shape {}", shape_type)),
|
_ => Err(miette!("Unknown shape {}", shape_type)),
|
||||||
|
|
@ -586,10 +489,10 @@ enum ShapeType {
|
||||||
p: Vec<Pos3>,
|
p: Vec<Pos3>,
|
||||||
},
|
},
|
||||||
Disk {
|
Disk {
|
||||||
height: f64,
|
height: Float,
|
||||||
radius: f64,
|
radius: Float,
|
||||||
innerradius: f64,
|
innerradius: Float,
|
||||||
phimax: f64,
|
phimax: Float,
|
||||||
},
|
},
|
||||||
PlyMesh {
|
PlyMesh {
|
||||||
filename: String,
|
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
|
where
|
||||||
T: std::str::FromStr,
|
T: std::str::FromStr,
|
||||||
<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 mut data = Vec::new();
|
||||||
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() == "["))
|
||||||
|
|
@ -270,16 +271,17 @@ impl Tokenizer {
|
||||||
.into());
|
.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
|
where
|
||||||
T: std::str::FromStr,
|
T: std::str::FromStr,
|
||||||
<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,
|
||||||
F: Fn(T, T) -> P,
|
F: Fn(T, T) -> P,
|
||||||
{
|
{
|
||||||
|
let mut data = Vec::new();
|
||||||
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() == "["))
|
||||||
|
|
@ -320,16 +322,17 @@ impl Tokenizer {
|
||||||
.into());
|
.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
|
where
|
||||||
T: std::str::FromStr,
|
T: std::str::FromStr,
|
||||||
<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,
|
||||||
F: Fn(T, T, T) -> P,
|
F: Fn(T, T, T) -> P,
|
||||||
{
|
{
|
||||||
|
let mut data = Vec::new();
|
||||||
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() == "["))
|
||||||
|
|
@ -380,8 +383,78 @@ impl Tokenizer {
|
||||||
.into());
|
.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)]
|
#[derive(Error, Debug, Diagnostic)]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue