Agregar readHex a un analizador de Parsec

I'm going through Write Yourself a Scheme and am struck at the exercise 4 on consulta en esta página.

How do I go about this? I've got this far, but have no idea whatsoever where the readHex is supposed to go, must I liftM it ? Do you case match the parser ?

parseNumber = liftM (Number . read) $ choice [many1 digit, char '#' >> oneOf "hd" >>= a]
    where a f = case f of
        'h' -> many1 digit

Also, I don't suppose you can apply <|> on Parser LispVal functions, right?

preguntado el 27 de agosto de 11 a las 16:08

2 Respuestas

I've got this far, but have no idea whatsoever where the readHex is supposed to go, must I liftM it ?

Si desde readHex is most likely a pure function and liftM lifts it into the monadic context of Parser.

Since I don't quite understand what your local function a is good for, I'm leaving it for now and simply use the functions parseDecimal y parseHex. In that case, you could write parseNumber al igual que:

parseNumber :: Parser LispVal
parseNumber = liftM Number $ parseDecimal <|> parseHex
    where parseDecimal :: Parser Integer
          parseDecimal = liftM read $ many1 digit
          parseHex :: Parser Integer
          parseHex     = liftM readHex $ char '#' >> ... -- parse hex value

Of course you can omit the type signatures, I just added them for clarity.

Also, I don't suppose you can apply <|> on Parser LispVal functions, right?

<|> works for every Parser a.

I recommend reading some material on parser combinators, i.e. the Parsec User Guide.

Respondido 27 ago 11, 23:08

It's not quite this straightforward, because of the type of readHex :: Num a => String -> [(a, String)]. Ver esta pregunta for how to deal with that. - Hammar

Thanks! Am closer to understanding now! Will accept your answer shortly if nothing else comes - sobresalir

Monadic parsing is very powerful and actually quite easy, once you worked it out. You might also want to have a look at Control.Applicative, which can lead to more consice parsers sometimes, and the current version of Parsec (because it seems that the tutorial is a bit outdated). - bzn

I've changed the layout a bit, but here is the code example we are considering:

parseNumber = 
    liftM (Number . read) $ 
        choice [many1 digit, char '#' >> oneOf "hd" >>= a]
    where
    a f =
        case f of
            'h' -> many1 digit

I think you are trying to do too many things at the same time before knowing how things should go together. You somehow have to move a readHex en el (Number . read) parte en lugar de read, depending on what type of digits were being read.

Here is a parser for hexadecimal numbers that doesn't use liftM or other fancy combinators:

parseHex :: Parser LispVal
parseHex = do
    char '#'
    char 'x'
    digits <- many1 hexDigit   -- hexDigit is defined by Parsec.
    return (Number (fst (readHex digits !! 0)))

The tricky part here is the extraction of the result of readHex.

You can combine parsers with

try parseHex <|> try parseOct <|> ... <|> parseDec

or get fancy with tuning and inlining and the many tools of Parsec. But I'd start with the basics.

Respondido 28 ago 11, 00:08

Regarding the last code snippet - don't forget the 1st rule of of Parsec - "try to avoid try". - Stephen Tetley

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.