Bogdanp/elm-combine/1.1.0

NOTE: this package is not compatible with Elm 0.19.1

Combine

This library provides reasonably fast parser combinators.

type Parser res
= Parser (ParseFn res)
| RecursiveParser (Lazy (ParseFn res))

A wrapper type around ParseFns used to differentiate between eager and lazy parsers.

type alias ParseFn res =
Context -> ( Result res, Context )

At their core, Parsers are functions from a Context to a tuple of a Result and a new Context.

myParseFn : ParseFn Int
myParseFn c = (Done 1, c)


myParser : Parser Int
myParser = Parser myParseFn


parse myParser "a" ==
  (Done 1, { input = "a", position = 0 })
type alias Context =
{ input : String
, position : Int
}

The contex over which ParseFns operate.

type Result res
= Done res
| Fail (List String)

Running a Parser results in one of two states:

  • Done when the parser has successfully parsed the input (or part of it) to a result, or
  • Fail when the parser has failed with a list of error messages.

Running a Parser

parse : Parser res -> String -> ( Result res, Context )

Parse a string.

app : Parser res -> Context -> ( Result res, Context )

Unwrap a parser so it can be applied to a context.

rec : (() -> Parser res) -> Parser res

Defer a parser's evaluation.

type E = ETerm String | EList (List E)


whitespace : Parser String
whitespace = regex "[ \t\r\n]*"


term : Parser E
term = ETerm `map` (whitespace *> regex "[a-zA-Z]+" <* whitespace)


list : Parser E
list = rec (\() -> EList `map` (string "(" *> many (term `or` list) <* string ")"))


parse list "" ==
  (Fail ["expected \"(\""], { input = "", position = 0 })


parse list "()" ==
  (Done (EList []), { input = "", position = 2 })


parse list "(a (b c))" ==
  (Done (EList [ETerm "a", EList [ETerm "b", ETerm "c"]]), { input = "", position = 9 })

Transforming Parsers

(res -> res_)
-> (List String -> List String)
-> Parser res
-> Parser res_

Transform both the result and error message of a parser.

map : (res -> res_) -> Parser res -> Parser res_

Transform the result of a parser.

parse (map String.toUpper (string "a")) "a" ==
  (Done "A", { input = "", position = 1 })
mapError : (List String -> List String) -> Parser res -> Parser res

Transform the error of a parser.

parse (mapError (\_ -> ["bad input"]) (string "a")) "b" ==
  (Fail ["bad input"], { input = "b", position = 0 })

Chaining Parsers

andThen : Parser res -> (res -> Parser res_) -> Parser res_

Sequence two parsers by passing in the results of the first parser to the second.

andMap : Parser (res -> res_) -> Parser res -> Parser res_

Sequence two parsers.

import Maybe
import Result
import String


num : Parser Int
num = (Maybe.withDefault 0 << Result.toMaybe << String.toInt) `map` regex "[0-9]+"


sum : Parser Int
sum = (+) `map` (num <* string "+") `andMap` num


parse sum "1+2" ==
  (Done 3, { input = "", position = 3 })

Parsers

fail : List String -> Parser res

Fail without consuming any input.

succeed : res -> Parser res

Return a value without consuming any input.

parse (succeed 1) "a" ==
  (Done 1, { input = "a", position = 0 })
string : String -> Parser String

Parse an exact string match.

parse (string "hello") "hello world" ==
  (Done "hello", { input = " world", position = 5 })


parse (string "hello") "goodbye" ==
  (Fail ["expected \"hello\""], { input = "goodbye", position = 0 })
regex : String -> Parser String

Parse a Regex match.

Regular expressions must match from the beginning of the input and their subgroups are ignored. A ^ is added implicitly to the beginning of every pattern unless one already exists.

parse (regex "a+") "aaaaab" ==
  (Done "aaaaa", { input = "b", position = 5 })
while : (Char -> Bool) -> Parser String

Consume input while the predicate matches.

parse (while ((/=) ' ')) "test 123" ==
  (Done "test", { input = " 123", position = 4 })
end : Parser ()

Fail when the input is not empty.

parse end "" == (Done (), { input = "", position = 0 })
parse end "a" == (Fail ["expected end of input"], { input = "a", position = 0 })
or : Parser res -> Parser res -> Parser res

Choose between two parsers.

parse (string "a" `or` string "b") "a" ==
  (Done "a", { input = "", position = 1 })


parse (string "a" `or` string "b") "b" ==
  (Done "b", { input = "", position = 1 })


parse (string "a" `or` string "b") "c" ==
  (Fail ["expected \"a\"", "expected \"b\""], { input = "c", position = 0 })
choice : List (Parser res) -> Parser res

Choose between a list of parsers.

parse (choice [string "a", string "b"]) "a" ==
  (Done "a", { input = "", position = 1 })


parse (choice [string "a", string "b"]) "b" ==
  (Done "b", { input = "", position = 1 })
optional : res -> Parser res -> Parser res

Return a default value when the given parser fails.

letterA : Parser String
letterA = optional "a" (string "a")


parse letterA "a" == (Done "a", { input = "", position = 1 })
parse letterA "b" == (Done "a", { input = "b", position = 0 })
maybe : Parser res -> Parser (Maybe res)

Wrap the return value into a Maybe. Returns Nothing on failure.

parse (maybe (string "a")) "a" ==
  (Done (Just "a"), { input = "", position = 1 })


parse (maybe (string "a")) "b" ==
  (Done Nothing, { input = "b", position = 0 })
many : Parser res -> Parser (List res)

Apply a parser until it fails and return a list of the results.

parse (many (string "a")) "aaab" ==
  (Done ["a", "a", "a"], { input = "b", position = 3 })


parse (many (string "a")) "" ==
  (Done [], { input = "", position = 0 })
many1 : Parser res -> Parser (List res)

Parse at least one result.

parse (many1 (string "a")) "a" ==
  (Done ["a"], { input = "", position = 1 })


parse (many1 (string "a")) "" ==
  (Fail ["expected \"a\""], { input = "", position = 0 })
sepBy : Parser x -> Parser res -> Parser (List res)

Parser zero or more occurences of one parser separated by another.

parse (sepBy (string ",") (string "a")) "b" ==
  (Done [], { input = "b", position = 0 })


parse (sepBy (string ",") (string "a")) "a,a,a" ==
  (Done ["a", "a", "a"], { input = "", position = 5 })


parse (sepBy (string ",") (string "a")) "a,a,b" ==
  (Done ["a", "a"], { input = ",b", position = 3 })
sepBy1 : Parser x -> Parser res -> Parser (List res)

Parse one or more occurences of one parser separated by another.

skipMany : Parser x -> Parser ()

Apply a parser and skip its result many times.

skipMany1 : Parser x -> Parser ()

Apply a parser and skip its result at least one.

chainl : Parser res -> Parser (res -> res -> res) -> Parser res

Parse one or more occurences of p separated by op, recursively apply all functions returned by op to the values returned by p. See the examples/Calc.elm file for an example.

chainr : Parser res -> Parser (res -> res -> res) -> Parser res

Similar to chainl but functions of op are applied in right-associative order to the values of p.

count : Int -> Parser res -> Parser (List res)

Parse n occurences of p.

between : Parser left -> Parser right -> Parser res -> Parser res

Parse something between two other parsers.

The parser

between (string "(") (string ")") (string "a")

is equivalent to the parser

string "(" *> string "a" <* string ")"
parens : Parser res -> Parser res

Parse something between parentheses.

brackets : Parser res -> Parser res

Parse something between brackets.

squareBrackets : Parser res -> Parser res

Parse something between square brackets.