Parsing with Megaparsec

Contents
A parser is a value
Combinators
Running a parser
Labels and errors

A parser is a value

A parser is a function from text to a result plus the rest of the input, wrapped in a type that also carries an error. Megaparsec gives that type a name and a set of combinators for building bigger parsers out of small ones:

import Text.Megaparsec import Text.Megaparsec.Char import Data.Void (Void) type Parser = Parsec Void String

Void means this parser never produces a custom error type, and String is the input type. A Text input is faster, and the code below is otherwise identical.

Combinators

Six combinators build most grammars. Each one consumes input on success and leaves it untouched on failure, which is what makes them compose:

char 'x' -- one specific character string "let" -- a literal string some digitChar -- one or more digits many letterChar -- zero or more letters sepBy p (char ',') -- p, separated by commas eof -- nothing left to read

some fails on an empty match and many succeeds with an empty list, so the choice between them is a decision about whether an empty value is legal.

number :: Parser Int number = read <$> some digitChar csvLine :: Parser [String] csvLine = sepBy (some (noneOf ",\n")) (char ',')

noneOf accepts any character except the ones in the string. That single line parses a CSV row, and adding endBy over newlines parses the whole file.

Running a parser

parse takes a parser, a name for the source, and the input. The name appears in error messages, so use the real filename:

runNumber :: String -> Either String Int runNumber s = case parse (number <* eof) "input" s of Left err -> Left (errorBundlePretty err) Right n -> Right n

The <* eof is not optional in practice. Without it, parse number "12abc" succeeds with 12 and silently ignores the rest, which is a bug that survives testing.

ghci> runNumber "12" Right 12 ghci> runNumber "12abc" Left "input:1:3:\n |\n1 | 12abc\n | ^\nunexpected 'a'\n"

The error points at the column where the parser stopped expecting more input, which is usually exactly where the problem is.

Labels and errors

A raw combinator error mentions characters. A label mentions the concept, and the message becomes something a user can act on:

identifier :: Parser String identifier = (some (alphaNumChar)) <?> "an identifier" -- unexpected '!' -- expecting an identifier

<?> replaces the expectations of the parser on its left when it fails without consuming input. Put it on every rule a user can trigger, and leave the internal helpers bare.

-- a tiny config grammar setting = (,) <$> identifier <*> symbol "=" <*> number symbol s = string s <* space

A symbol helper that consumes trailing space is the standard trick for making whitespace invisible in the grammar. The capstone in the next lesson uses that idea for its command line.

Next: Capstone: a todo CLI

Article author: Arthur Isaev

Related articles
Haskell
Haskell course: zero to hero in 50 lessons
What is Haskell and why it matters
Install the toolchain with GHCup
GHCi: the interactive shell
Your first program
Values with let and where
Types and type signatures
Functions and application
Currying and partial application
Operators, sections, and fixity
if, guards, and pattern guards
case expressions
Pattern matching
Tuples
Lists
Ranges and enumeration
List comprehensions
Recursion replaces loops
map, filter, and foldr
Lambdas and composition
Type classes
Parametric polymorphism
Maybe and null safety
Either and recoverable errors
data: product types
data: sum types
Records
newtype and type synonyms
Recursive data: binary trees
Functor
Applicative
Monad
do notation
IO and purity
Files and IO
Modules and imports
Cabal projects
Dependencies
Testing with HUnit
Property testing with QuickCheck
Laziness and infinite lists
Strictness and space leaks
Folds
Monoids
Traversable
Monad transformers
Errors and exceptions
Concurrency
Parsing with Megaparsec
Capstone: a todo CLI
Hero roadmap: the whole course on one page

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.com
Write to info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: