I'm using Parsec with a custom Stream
type. This stream is essentially String
but sometimes it expands input it finds in the string into other strings (think alias expansion). For example, given "§4.1 ¶3" it might feed "Section 4.1 paragraph 3" to the parser.
I have this all working. My types look like:
data DealiasingStream = ...
instance (Monad m) => Stream DealiasingStream m Char where ...
type ShellParser = Parsec DealiasingStream ()
Notice that the dependent type of DealiasingStream
is just Char
. This allows my parsers (well, my ShellParser
s to use all the standard character parsers.
My question is about getting Parsec to report the positions in terms of the original input to my stream. The documentation for Stream
says:
A
Stream
instance is responsible for maintaining the "position within the stream" in the stream states
. This is trivial unless you are using the monad in a non-trivial way.
Indeed, my stream type knows what position it wants to report at any given moment... but I don't see how to get Parsec to use it! Parsec seems to maintain its own SourcePos
as part of its internal State
. And this seems to be updated by the various token
prims, and hence for the standard Char
parsers, out of my control.
How is one supposed to do this?
I agree with your understanding — there's no easy way to control the position without rewriting functions such as char
.
What the documentation means is that the Stream
instance is responsible for recording the position information inside the tokens. That information then can be used in functions like token
or tokenPrim
(by supplying appropriate position-calculating functions to them).
Thus, you have to wrap Char
into a datatype that includes position information and rewrite the basic functions using the primitives like token
or tokenPrim
that are flexible about the position calculation.