Hi Dan,<br><br>thank you for the solution. It looks pretty interesting and usable, however I'll have to spend some time understanding arrows: I never had an opportunity to use them before. Anyway, it looks very close to what I actually need, and in any case much less ugly than breaking the GenParser encapsulation<br>
<br><div class="gmail_quote">2009/8/6 Dan Weston <span dir="ltr"><<a href="mailto:westondan@imageworks.com">westondan@imageworks.com</a>></span><br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Of course, since ParsecT s u m is a functor, feel free to use fmap instead of parsecMap. Then you don't need to import from Text.Parsec.Prim.<br>
And in hindsight, I might prefer the name (<:>) or cons to (<>) for the first function, but now I'm just obsessing. :)<br><font color="#888888">
<br>
Dan</font><div><div></div><div class="h5"><br>
<br>
Dan Weston wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
I think parsecMap does the job here:<br>
<br>
-----------------------<br>
import Text.ParserCombinators.Parsec hiding ((<|>))<br>
import Text.Parsec.Prim(parsecMap)<br>
import Control.Applicative((<|>))<br>
import Control.Arrow((|||),(&&&))<br>
<br>
-- Tagged (:)<br>
(<>) :: Either Char Char -> Either String String -> Either String String<br>
Left a <> Left b = Left (a:b)<br>
Left a <> Right b = Left (a:b)<br>
Right a <> Left b = Left (a:b)<br>
Right a <> Right b = Right (a:b)<br>
<br>
-- Tagged concat<br>
stringParser :: [Either Char Char] -> Either String String<br>
stringParser = foldr (<>) (Right "")<br>
<br>
-- Parse Integer if properly tagged, keeping unparsed string<br>
maybeToInteger :: Either String String -> (Maybe Integer, String)<br>
maybeToInteger = (const Nothing ||| Just . read) &&& (id ||| id)<br>
<br>
-- Tagged-choice parser<br>
intOrStringParser = parsecMap (maybeToInteger . stringParser)<br>
$ many1 (parsecMap Right digit <|> parsecMap Left (noneOf ";)"))<br>
<br>
-- Parse between parentheses<br>
intOrStringListParser = between (char '(')<br>
(char ')')<br>
(sepBy1 intOrStringParser (char ';'))<br>
-----------------------<br>
<br>
Then you get a tagged version of each string, along with the string itself:<br>
<br>
*P> parseTest intOrStringListParser $ "(1;2w4;8;85)"<br>
[(Just 1,"1"),(Nothing,"2w4"),(Just 8,"8"),(Just 85,"85")]<br>
<br>
There may be some parsecMap-fold fusion optimization possible, though I haven't looked into that.<br>
<br>
Dan<br>
<br>
Paul Sujkov wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Hi everybody,<br>
<br>
suppose I have two different parsers: one just reads the string, and another one parses some values from it. E.g.:<br>
<br>
parseIntList :: Parser [Integer]<br>
parseIntList = do<br>
char '('<br>
res <- liftM (map read) (sepBy1 (many1 digit) (char ';'))<br>
char ')'<br>
return res<br>
<br>
parseIntString :: Parser String<br>
parseIntString = manyTill anyChar eof<br>
<br>
so for some input like this - "(1;2;3;4)" - I will have two different result:<br>
<br>
*Parlog> parseTest parseIntList "(1;2;3;4)"<br>
[1,2,3,4]<br>
*Parlog> parseTest parseIntString "(1;2;3;4)"<br>
"(1;2;3;4)"<br>
<br>
but the thing that I actually want is something like Parser ([Integer], String) - results from both parsers at a time, no matter whether one of them fails or not:<br>
<br>
*Parlog> parseTest parseIntListAndString "(1;2;3;4)"<br>
([1,2,3,4], "(1;2;3;4)")<br>
<br>
it is impossible at first sight, because first parser to use will consume all the input, and there will be nothing to parse for the second one<br>
<br>
Parsec contains "choice" function, but it is implemented via <|> and that is mplus - so it tries second alternative only if the first one fails. Is it possible to use two parsers for the same string (with try-like backtracking, no input actually consumed till the second parser finishes)? I can assume only dirty hacks with the GenParser internals - manual position storing and backtracking - but that is obviously not good<br>
<br>
however, my first attempt to solve the problem was kind a like that: to parse string to String, and then to use it as an input for the next level parse call:<br>
<br>
parseIntListAndString :: Parser ([Integer], String)<br>
parseIntListAndString = do<br>
str <- parseIntString<br>
return (res str, str)<br>
where res str = case (parse parseIntList "" str) of<br>
Left err -> []<br>
Right val -> val<br>
<br>
but the problems with such a method began when I switched from Parser to GenParser with user state: function parseIntList have to update the state, but it can't have the same state as the parseIntListAndString any more: it has it's own. I can explicitly pass the state from parseIntListAndString to parseIntList, but I see no suitable way for the parseIntList to update it. I can return the updated state value from the parseIntList function, and call setState on a result - but it seems rather ugly to mee. However, if nothing else will do, that is an alternative<br>
<br>
it is of course possible to use two different parsers sequentially, but it is also very ineffective: I need to use such multiple parsing on a relatively small substring of the actual input, so little backtracking would be a much nicier approach. Any suggestions?<br>
<br>
-- <br>
Regards, Paul Sujkov<br>
<br>
</blockquote>
<br>
</blockquote>
<br>
</div></div></blockquote></div><br><br clear="all"><br>-- <br>Regards, Paul Sujkov<br>