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

Weird bind implementation? #33

Open
deliciouslytyped opened this issue Dec 4, 2019 · 7 comments
Open

Weird bind implementation? #33

deliciouslytyped opened this issue Dec 4, 2019 · 7 comments

Comments

@deliciouslytyped
Copy link

deliciouslytyped commented Dec 4, 2019

Hi, thanks for making parsy, I like it!

I'm currently succeeding at shooting myself in the foot with heterogeneous inputs, because I want to be able to process a list of "symbols" and strings:

#some context for the following snippets
from dataclasses import dataclass

@dataclass
class Indent:
  depth: int

@generate
def indent():
  return (yield test_item(lambda i: isinstance(i, Indent), "not indent"))

sideToken = indent

newline = string("\r\n")
preline_ = (newline.should_fail("not newline") >> any_char).many().concat()
line = preline_ << newline
take = test_item(lambda x: True, "any nested")

I'm probably not understanding something, why do I need to use .bind like this?:

>>> wrap = lambda fn: lambda res: success(fn.parse(res))
>>> ( sideToken >> take.bind(wrap(line)) ).many()
        .parse([Indent(0), "asd\r\n", Indent(1), "asdf\r\n"])
['asd', 'asdf']

instead of: (the following is pseudocode)

>>> ( sideToken >> take.bind(line) ).many() \
        .parse([Indent(0), "asd\r\n", Indent(1), "asdf\r\n"])
['asd', 'asdf']

Which is to say, why do I have to add an extra layer of wrapping and calling .parse?

@deliciouslytyped
Copy link
Author

Related: I seem to have to define wrap something like the following to get backtracking to work?

def wrap(fn):
  def thing(res):
    try:
      result = fn.parse(res)
      return success(result)
    except:
      return fail("idk")
  return thing

What's the correct way to do this?

@spookylukey
Copy link
Member

@jneen do you have any input on this? I've never actually used bind directly, perhaps you could answer this question more easily than me?

@deliciouslytyped
Copy link
Author

Ok, this makes more sense when used like in #36 (comment) , I need to revisit this later.

@jneen
Copy link
Contributor

jneen commented Dec 12, 2019

I'm not sure what you're trying to do, could you explain in a little more detail? The "pseudocode" example you gave passes a Parser instance to .bind - but .bind takes a function that returns a parser. It's a little hard to follow because your variable named fn is not actually a function, but a parser.

@jneen
Copy link
Contributor

jneen commented Dec 12, 2019

Oh, I think I see what's happening. You are mixing strings and tokens, and want to "re-parse" the parse result when it's not a token. I... would suggest just tokenizing your entire input before passing it to parsy if that's the direction you want to go.

@jneen
Copy link
Contributor

jneen commented Dec 12, 2019

If you are really dedicated to this approach you might want to define something like:

def reparse(parser):
    return test_item(lambda x: isinstance(x, unicode), 'not a string').map(parser.parse)

@jneen
Copy link
Contributor

jneen commented Dec 12, 2019

@spookylukey for reference, bind is just a way to dynamically determine the next parser, which you can naturally do with @generate functions. These are equivalent:

parser = first.bind(second)

@generate
def parser():
    res = yield first
    return (yield second(res))

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

3 participants