How to also get how many characters read in parse?

Viewed 65

I'm using Numeric.readDec to parse numbers and reads to parse Strings. But I also need to know how many characters were read.

For example readDec "52 rest" returns [(52," rest")], and read 2 characters. But there isn't a great way that I can find to know that it read 2 characters.

You could check the string length of show 52, but if the input was 052 that would give you the wrong answer (this solution also wouldn't work for the string parsing which has escape characters). You also could use the length of the post parsed string subtracted from the length of the input string. But this is very inefficient for long strings with many parses.

How can this be done correctly and efficiently (preferably without just writing your own parse)?

1 Answers

With just base, instead of readDec, you can use readDecP from Text.Read.Lex, which uses a ReadP parser:

readDecP :: (Eq a, Num a) => ReadP a

The gather combinator in Text.ParserCombinators.ReadP returns the parse result along with the actual characters parsed:

gather :: ReadP a -> ReadP (String, a)

You can run the parser with readP_to_S, which gives back a ReadS parser, which is a function that accepts a string and produces a list of possible parses with the remainder of the string.

readP_to_S :: ReadP a -> ReadS a

type ReadS a = String -> [(a, String)]

An example in GHCi:

> import Text.ParserCombinators.ReadP (gather, readP_to_S)

> import Text.Read.Lex (readDecP)

> readP_to_S (gather readDecP) "52 rest"
[(("52",52)," rest")]

> readP_to_S (gather readDecP) "0644 permissions"
[(("0644",644)," permissions")]

You can simply check that there is only one valid parse if you want the result to be unambiguous, and then take the length of the first component to find the number of Char code points parsed.

These parsers are fairly limited, however; if you want something easier to use, faster, or able to produce more detailed error messages, then you should check out a more fully featured parsing package such as regex-applicative (regular grammars) or megaparsec (context-sensitive grammars).

Related