Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using generics on grammar level #333

Open
LordBlackhawk opened this issue Feb 4, 2023 · 2 comments
Open

Using generics on grammar level #333

LordBlackhawk opened this issue Feb 4, 2023 · 2 comments
Labels
feature Something not supported that perhaps should be

Comments

@LordBlackhawk
Copy link

I am trying to make the parser result generic and reuse the same parser for different tasks. The following variant is working:

pub trait Factory {
    type Node;
    fn build_number(&mut self, value: u32) -> Self::Node;
}

peg::parser! {
    grammar working() for str {
        pub rule number<T: Factory>(factory: &mut T) -> T::Node
            = n:$(['0'..='9']+) {?
                Ok(factory.build_number(n.parse().or(Err("u32"))?))
            }
        // more rules all having factory argument...
    }
}

But there is much boiler plate, always mentioning the Factory argument. Especially calling other rules always needs mentioning the factory argument there, too.

I would have expected that the following variant works, but on a grammar only life time generics may be specified:

peg::parser! {
    grammar not_working<T: Factory>(factory: &mut T) for str {
        pub rule number() -> T::Node
            = n:$(['0'..='9']+) {?
                Ok(factory.build_number(n.parse().or(Err("u32"))?))
            }
        // more rules all having factory argument...
    }
}

I tried also the following variant, but I was unable to specify the return type of the rules:

peg::parser! {
    grammar almost_working(factory: &mut impl Factory) for str {
        pub rule number() -> ???::Node
            = n:$(['0'..='9']+) {?
                Ok(factory.build_number(n.parse().or(Err("u32"))?))
            }
        // more rules all having factory argument...
    }
}

Is there a way to realize this?
Would it be possible to allow type generics on the grammar itself?

@kevinmehall
Copy link
Owner

I believe this would be possible to add, but would make the signatures of the rule functions more confusing. Generics can't be attached to a mod, so they're prepended to the generics list of the functions corresponding to rules generated within it:

grammar test1<'g>() for str {
  pub rule foo<'r, R>() = ...
}

->

mod test1 {
    pub fn foo<'g, 'r, R>(&'input str) {...}
}

If we allow type (and / or const) params on the grammar, they must be reordered in the function definition so all lifetimes come before types:

grammar test1<'g, G>() for str {
  pub rule foo<'r, R>() = ...
}

->

mod test1 {
    pub fn foo<'g, 'r, G, R>(&'input str) {...}
}

I skipped that complexity because I didn't see a need for grammar type params, but I think it could be added.

@LordBlackhawk
Copy link
Author

LordBlackhawk commented Feb 6, 2023

Would be nice to have, but if I would be the only user, you should maybe not do it...

By the way, I could remove some of the boilerplate by using precedence!{ ... }. A very nice feature. I could remove more than I initially expected.

@LordBlackhawk LordBlackhawk reopened this Feb 6, 2023
@kevinmehall kevinmehall added the feature Something not supported that perhaps should be label Oct 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Something not supported that perhaps should be
Projects
None yet
Development

No branches or pull requests

2 participants