1
votes

As part of trying to write a JSON Parser, I'm working on parsing a JSON String value.

Given the following definition from Prof. Brent Yorgey's Haskell course:

-- A parser for a value of type a is a function which takes a String
-- represnting the input to be parsed, and succeeds or fails; if it
-- succeeds, it returns the parsed value along with the remainder of
-- the input.
newtype Parser a = Parser { runParser :: String -> Maybe (a, String) }

I'm running into trouble since, as I understand, my second parser, zeroOrMore notEndOfString, simply consumes every token until reaching the end of the String.

data JValue = S String | ...

parseStringJValue :: Parser JValue
parseStringJValue = S <$> ((char '"') *> (zeroOrMore notEndOfString) <* (char '"'))

notEndOfString :: Parser Char
notEndOfString = Parser f
  where 
    f []     = Nothing
    f (x:xs) = Just (x, xs)

Using this applicative approach, please give me a hint towards modifying my above parseStringJValue to actually work.

Note - I have a faint idea on how to solve it with Monads, but I'm not sure if if they are required.

1
Hint: the String in notEndOfString is the result of your parse, not the input to be parsed. - genisage
You probably want zeroOrMore notEndOfStringOrQuote. In parsec, for example, I would probably just write many (noneOf "\""). - user2407038

1 Answers

1
votes

notEndOfString is matching and consuming any characters in the text including " until the end, so it acts greedily to consume all characters and never hands off to the char '"' parser. You need to match any characters inside of the quotes that aren't a quote, which means you need the negation of char.

If you have char then presumably you built it from satisfy, which means you can also define notChar:

notChar c = satisfy (/= c)

(Note, by the way, that notEndOfString is just satisfy id). Otherwise, you can write it similarly to char.

Then you can define the parser for strings by building up a few other useful combinators:

bracket l r p = l *> p <* r
quotes = bracket (char '"') (char '"')

parseStringJValue = S <$> quotes (zeroOrMore (notChar '"'))

Note that this won't handle escapes ("\"") within quotes.


Edit: To enforce the invariant that is implicit above, it would be nice to define

inside l r = bracket (char l) (char r) (zeroOrMore (notChar r))

Then parseStringJValue = S <$> inside '"' '"'.