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 a custom input struct that just wraps an input &str #1540

Open
gwennlbh opened this issue Aug 3, 2022 · 10 comments
Open

Using a custom input struct that just wraps an input &str #1540

gwennlbh opened this issue Aug 3, 2022 · 10 comments

Comments

@gwennlbh
Copy link

gwennlbh commented Aug 3, 2022

Prerequisites

  • Rust version : rustc 1.62.1 (e092d0b6b 2022-07-16)
  • nom version : 7.1.1
  • nom compilation features used: none

The problem

Sometimes, a custom input type is necessary to store data alongside the input string. I haven't found a way to quickly
implement the required 13 traits.

Manual implementation is required when the input type is completely novel, but for cases where the custom input struct merely wraps an input string,
with additional data fields having their values passed around as-is, this manual implementation should be avoidable.

I know that the aforementionned documentation links to fflorent/nom_locate, and references this very concept
of "wrapping" an input string with a custom struct, but, looking at their implementation, manual re-implementation seems necessary:

impl<T, U, X> FindSubstring<U> for LocatedSpan<T, X>
where
    T: FindSubstring<U>,
{
    #[inline]
    fn find_substring(&self, substr: U) -> Option<usize> {
        self.fragment.find_substring(substr)
    }
}

for example.

An example use case

To eliminate left-recursion, I wanted to attach additional data to disable certain parsers:

fn expression(input: Input) -> Result<Expression> {
...
	if let Ok((...)) = tuple((...)) { ... }
...
}

...

fn operation(input: Input) -> Result<Operation> {
	if input.disable_operation {
	    return Err(...);
        }
	let input = Input {
	    disable_operation: true,
	    ..input
        };
	... = tuple((expression, ...))
}

I ended up using a second argument, and binding the additional data struct to the parser to create a unary function that is compatible with nom's combinators:

fn expression(state: InputState, input: &str) -> Result<Operation> {
	let operation = |i| operation(state, i);
	....
}

This is fine for now, because I only have 5 functions that need this additional data, but I want to add a call depth number to this state for debugging purposes, which would force me to bind state like this in all parsers, for all parsers.

A solution

Is there a way to tell nom that this "custom input type" is a struct that simply wraps an &str?

Something like this:

#[nom::wraps(input)]
struct Input {
    disable_operation: bool
    disable_relationship: bool
    call_depth: usize
    input: &str
}

(I don't know if this API suggestion makes sense or is even possible, I'm just suggesting this to explicit what I want)

@Xiretza
Copy link
Contributor

Xiretza commented Aug 3, 2022

Adding a macro seems overkill - how about if nom provided a

struct StatefulStr<'a, T> {
    input: &'a str,
    state: T,
}

that implements the relevant traits by ignoring state? Your Input would then look like this:

struct RecursionState {
    disable_operation: bool
    disable_relationship: bool
    call_depth: usize
}

type Input<'a> = StatefulStr<'a, RecursionState>;

@Xiretza
Copy link
Contributor

Xiretza commented Aug 4, 2022

It could even be generalized to this:

struct StatefulInput<I, T> {
    input: I,
    state: T,
}

I'll wait for an opinion from @Geal - if this sounds good, I'll work up a PR.

@mokeyish
Copy link

mokeyish commented Feb 9, 2023

Hello, what is the situation now?

@mokeyish
Copy link

mokeyish commented Feb 9, 2023

The solution to quickly
implement the required 13 traits.

impl<'a> Deref for Input<'a> {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        self.input
    }
}

@epage
Copy link
Contributor

epage commented Feb 9, 2023

Huh, seems like pori::Stateful has the same problem of not implementing all traits (also missing FindSubslice)

@Geal
Copy link
Collaborator

Geal commented Feb 9, 2023

The stateful wrapper is something that will happen, yes. In the meantime, I'm trying to reduce the amount of work needed to implement input types, like with this PR #1612

@mokeyish
Copy link

mokeyish commented Feb 9, 2023

I need an option to control the parser strategy.
eg: add extended parsers or disable some parsers.

What is the best practice? 🤣

@Geal
Copy link
Collaborator

Geal commented Feb 9, 2023

What do you mean by parser strategy?

@mokeyish
Copy link

mokeyish commented Feb 9, 2023

What do you mean by parser strategy?

I mean how to implement markdown extensions by nom.

https://github.com/wooorm/markdown-rs#extensions

@Geal
Copy link
Collaborator

Geal commented Feb 9, 2023

Right now, that would be an argument that is passed at parser creation, or the stateful wrapper

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants