Using Parsec to Write a Read Instance

Using Parsec, I can easily write a function like String -> Maybe MyType . Now I would like to create a Read instance for my type based on this; however, I do not understand how readsPrec works or what it should do.

My best guess right now is that readsPrec used to create a recursive parser from scratch to traverse a string, creating the desired data type in Haskell. However, I already have a very strong parser that does this for me. So, how do I tell readsPrec use my parser? What is the "operator precedence" setting that it accepts, and what is it good in my context?

If this helps, I created a minimal example on Github . It contains a type, a parser, and an empty Read instance and reflects well where I am stuck.

(Background: The real parser is for the circuit.)

+6
source share
1 answer

However, I already have a very strong parser that does this for me.

Actually, it’s not so cool that your parser has problems with extra parentheses, it will not parse

 ((1) (2)) 

and it will throw an exception on some distorted inputs because

 singleP = Single . read <$> many digit 

may use read "" :: Int .

In this case, the priority argument is used to determine whether the brackets are needed anywhere, for example. if you have

 infixr 6 :+: data a :+: b = a :+: b data C = C Int data D = DC 

you do not need parentheses around C 12 as an argument (:+:) , since the application priority is higher than that of (:+:) , but you need parentheses around C 12 as an argument D

So you usually have something like

 readsPrec p = needsParens (p >= precedenceLevel) someParser 

where someParser parses the value from input without conclusions in parentheses, and needsParens True thing parses a thing between parentheses, and needsParens False thing parses thing , optionally enclosed in parentheses [you should always take more brackets than necessary, ((((((1)))))) should be disassembled as Int ].

Since readsPrec p analyzers readsPrec p used to parse input parts as parts of a value when reading lists, tuples, etc., they should return not only the parsed value, but also the rest of the input.

So an easy way to convert a parsec parser to a readsPrec parser would be

 withRemaining :: Parser a -> Parser (a, String) withRemaining p = (,) <$> p <*> getInput parsecToReadsPrec :: Parser a -> Int -> ReadS a parsecToReadsPrec parsecParser prec input = case parse (withremaining $ needsParens (prec >= threshold) parsecParser) "" input of Left _ -> [] Right result -> [result] 

If you use GHC, it might be preferable to use the ReadPrec / ReadP (built using Text.ParserCombinators.ReadP[rec] ) instead of the parsec parser and define readPrec instead of readsPrec .

+2
source

All Articles