stchang / parsack

A basic Parsec-like monadic parser combinator library implementation in Racket.
MIT License
50 stars 10 forks source link

"User" state? #29

Closed greghendershott closed 10 years ago

greghendershott commented 10 years ago

I have a need to keep track of context (specifically, whether I'm in quotes and whether they're single or double). I reached for Racket parameters, the normal tool for this job. However those don't work in the face of lazy and force, AFAICT.

As a result, I think what I want to do is extend the State that's threaded through the parser. But Parsack isn't designed to support that via (say) struct inheritance. Maybe it could support it via one extra user field added to State, which it simply respects and copies. If the user needs more than one value, they use a hash or put their own struct in there.

Any other ideas? And, if you have any ideas that would not require modifying Parsack, I'm definitely open to those as well.

stchang commented 10 years ago

So the problem is that the parameter is no longer in scope when you go to force the suspended computation?

Let me think about the best solution. Maybe struct properties would be better than a separate field? Let me know if you have other ideas as well.

greghendershott commented 10 years ago

So the problem is that the parameter is no longer in scope when you go to force the suspended computation?

Yes, as best I understand.

For example if I stick the following at the top of parsack.rkt

(define lazy values)
(define force values)

Then the parameters have the expected values. (But of course just a quick hack experiment. The parser is much slower as a result.)

Maybe struct properties would be better than a separate field?

Could be, that hadn't occurred to me.

Let me know if you have other ideas as well.

Will do. Thanks!

greghendershott commented 10 years ago

I did a bit of homework and it looks like Parsec provides "user state" combinators:

http://hackage.haskell.org/package/parsec-2.1.0.1/docs/src/Text-ParserCombinators-Parsec-Prim.html

-----------------------------------------------------------
-- User state combinators
-----------------------------------------------------------
getState :: GenParser tok st st
getState        = do{ state <- getParserState
                    ; return (stateUser state)
                    }

setState :: st -> GenParser tok st ()
setState st     = do{ updateParserState (\(State input pos _) -> State input pos st)
                    ; return ()
                    }

updateState :: (st -> st) -> GenParser tok st ()
updateState f   = do{ updateParserState (\(State input pos user) -> State input pos (f user))
                    ; return ()
                    }

Basically, a key/value store -- such as (hasheq/c symbol? any/c) -- would just set and get functions is all I would need.

greghendershott commented 10 years ago

Just a quick initial sketch, but, perhaps it would be nearly as simple as this (plus tests)?

I could work on this more and submit a PR, unless you'd like to take it on?

stchang commented 10 years ago

Your changes look reasonable. If it works for you, then go ahead and commit.

One thought: Maybe the user field should be optional? So you don't have to deal with it if you don't want. You would still have to propagate it in all the other places though (like you already did).

greghendershott commented 10 years ago

One thought: Maybe the user field should be optional? So you don't have to deal with it if you don't want.

I'm not sure what you mean, how to do that.

Also, unless a Parsack user has made use of the State struct definition, e.g. in a match or using the State ctor, this won't break existing code. But if they have, it will. (In hindsight maybe Parsack shouldn't have done (provide (all-defined-out))?)

stchang commented 10 years ago

Ok never mind I see what you are saying now. Thanks for the pull request!

stchang commented 10 years ago

PR #30 adds the feature.