davidcavazos / parser / Parser.Sequence

Parsers involving sequences of matches.

Sequences

sequence : List (Parser a) -> Parser (List a)

Matches a sequence of parsers in order, and gets the result as a List.

import Parser exposing (parse)
import Parser.Char exposing (char, digit, letter)

-- Note that all the parsers must be of the same type, like `Parser Char`.
sequence [ char '_', letter, digit ]
    |> parse "_A5" --> Ok [ '_', 'A', '5' ]

concat : List (Parser (List a)) -> Parser (List a)

Concatenates the parsed values from all the parsers into a single list with all the values.

import Parser exposing (parse)
import Parser.Char exposing (char, digit)

-- We get all these parsers as a single sequence of values.
concat
    [ oneOrMore digit       -- [ '3' ]
    , zeroOrOne (char '.')  -- [ '.' ]
    , zeroOrMore digit      -- [ '1', '4' ]
    ]
    |> parse "3.14"
--> Ok [ '3', '.', '1', '4' ]

maybe : Parser a -> Parser (Maybe a)

Matches an optional value and returns it as a Maybe.

ℹ️ Equivalent regular expression: ?

import Parser exposing (parse)
import Parser.Char exposing (letter)

-- Maybe we get `Just` a letter.
parse "abc" (maybe letter) --> Ok (Just 'a')

-- Or maybe we get `Nothing`.
parse "123abc" (maybe letter) --> Ok Nothing

zeroOrOne : Parser a -> Parser (List a)

Matches an optional value and returns it as a List.

ℹ️ Equivalent regular expression: ?

import Parser exposing (parse)
import Parser.Char exposing (letter)

-- We want one letter, optionally.
parse "abc" (zeroOrOne letter) --> Ok [ 'a' ]

-- If we don't get any, that's still okay.
parse "123abc" (zeroOrOne letter) --> Ok []

zeroOrMore : Parser a -> Parser (List a)

Matches zero or more values and returns them as a List.

ℹ️ Equivalent regular expression: *

import Parser exposing (parse)
import Parser.Char exposing (letter)

-- We want as many letters as there are.
parse "abc" (zeroOrMore letter) --> Ok [ 'a', 'b', 'c' ]
parse "abc123" (zeroOrMore letter) --> Ok [ 'a', 'b', 'c' ]

-- Even zero letters is okay.
parse "123abc" (zeroOrMore letter) --> Ok []

oneOrMore : Parser a -> Parser (List a)

Matches one or more values and returns them as a List.

ℹ️ Equivalent regular expression: +

import Parser exposing (parse)
import Parser.Char exposing (letter)
import Parser.Error

-- We want as many letters as there are.
parse "abc" (oneOrMore letter) --> Ok [ 'a', 'b', 'c' ]
parse "abc123" (oneOrMore letter) --> Ok [ 'a', 'b', 'c' ]

-- But we want at least one.
oneOrMore letter
    |> parse "123abc"
    |> Result.mapError Parser.Error.message
--> Err "1:1: I was expecting a letter [a-zA-Z]. I got stuck when I got the character '1'."

exactly : Basics.Int -> Parser a -> Parser (List a)

Matches a value exactly a number of times and returns them as a List.

ℹ️ Equivalent regular expression: {n}

import Parser exposing (parse)
import Parser.Char exposing (letter)
import Parser.Error

-- We want `exactly` three letters.
parse "abcdef" (exactly 3 letter) --> Ok [ 'a', 'b', 'c' ]

-- Not two or four, we want three.
exactly 3 letter
    |> parse "ab_def"
    |> Result.mapError Parser.Error.message
--> Err "1:3: I was expecting a letter [a-zA-Z]. I got stuck when I got the character '_'."

atLeast : Basics.Int -> Parser a -> Parser (List a)

Matches a value at least a number of times and returns them as a List.

ℹ️ Equivalent regular expression: {min,}

import Parser exposing (parse)
import Parser.Char exposing (letter)
import Parser.Error

-- We want at least three letters, we are okay with more than three.
parse "abcdef" (atLeast 3 letter) --> Ok [ 'a', 'b', 'c', 'd', 'e', 'f' ]

-- But not two, that's sacrilegious.
atLeast 3 letter
    |> parse "ab_def"
    |> Result.mapError Parser.Error.message
--> Err "1:3: I was expecting a letter [a-zA-Z]. I got stuck when I got the character '_'."

atMost : Basics.Int -> Parser a -> Parser (List a)

Matches a value at most a number of times and returns them as a List.

ℹ️ Equivalent regular expression: {0,max}

import Parser exposing (parse, andThenIgnore)
import Parser.Char exposing (char, letter)

-- We want a maximum of three letters.
parse "abcdef" (atMost 3 letter) --> Ok [ 'a', 'b', 'c' ]

-- Less than that is also okay.
parse "ab_def" (atMost 3 letter) --> Ok [ 'a', 'b' ]

-- Even zero letters are fine.
parse "_bcdef" (atMost 3 letter) --> Ok []

-- Make sure we don't consume more than three letters.
atMost 3 letter
    |> andThenIgnore (char 'd')
    |> parse "abcdef"
--> Ok [ 'a', 'b', 'c' ]

between : Basics.Int -> Basics.Int -> Parser a -> Parser (List a)

Matches a value between a range of times and returns them as a List.

ℹ️ Equivalent regular expression: {min,max}

import Parser exposing (parse)
import Parser.Char exposing (letter)
import Parser.Error

-- We want between two and four letters.
parse "abcdef" (between 2 4 letter) --> Ok [ 'a', 'b', 'c', 'd' ]
parse "abc_ef" (between 2 4 letter) --> Ok [ 'a', 'b', 'c' ]
parse "ab_def" (between 2 4 letter) --> Ok [ 'a', 'b' ]

-- But less than that is not cool.
between 2 3 letter
    |> parse "a_cdef"
    |> Result.mapError Parser.Error.message
--> Err "1:2: I was expecting a letter [a-zA-Z]. I got stuck when I got the character '_'."

until : Parser delimiter -> Parser a -> Parser ( List a, delimiter )

Matches a values repeatedly until a delimiter parser matches. The delimiter marks the end of the sequence, and it is not consumed.

import Parser exposing (drop, map, parse, succeed, take, textOf)
import Parser.Char exposing (char, letter)
import Parser.Check exposing (end)
import Parser.Error

-- Get all the letters until we find 'd'.
letter
    |> until (char 'd')
    |> parse "abcdef"
--> Ok ( [ 'a', 'b', 'c' ], 'd' )

-- But the delimiter _must_ be present.
letter
    |> until (char 'd')
    |> parse "abc123"
    |> Result.toMaybe
--> Nothing

-- The delimiter is consumed.
succeed identity
    |> drop (char '<')
    |> take (textOf (letter |> until (char '>') |> map Tuple.first))
    |> drop end
    |> parse "<abc>"
--> Ok "abc"

fold : (b -> a -> b) -> b -> Parser a -> Parser b

Reduces matches of a parser.

import Parser exposing (parse)
import Parser.Common exposing (letters, number, token)

parse "2 3 4" (fold (+) 1 (token number)) --> Ok 10
parse "b c d" (fold (++) "a" (token letters)) --> Ok "abcd"

foldWhile : (b -> a -> Maybe b) -> b -> Parser a -> Parser b

Reduces matches of a parser until a condition is met.

import Parser exposing (Parser, andThenIgnore, parse)
import Parser.Char exposing (char)
import Parser.Common exposing (number, token)

sumWhileLessThan : Float -> Parser Float
sumWhileLessThan max =
    foldWhile
        (\total x ->
            if total + x <= max then
                Just (total + x)
            else
                Nothing
        )
        0
        (token number)

-- The fold stops before we reach a maximum of 6 in the sum.
parse "2 3 4" (sumWhileLessThan 6) --> Ok 5

-- Make sure we didn't consume too many numbers.
sumWhileLessThan 6
    |> andThenIgnore (char '4')
    |> parse "2 3 4"
--> Ok 5

split : Parser separator -> Parser (List String)

Splits the input text by a separator parser into a List of Strings. The separators cannot overlap, and are discarded after being matched.

import Parser exposing (parse)
import Parser.Char exposing (char)

-- Split Comma-Separated-Values (CSV) into a `List` of `String`s.
split (char ',')
    |> parse "a,bc,def"
--> Ok [ "a", "bc", "def" ]

-- Leading/trailing separators are valid and give empty values.
split (char ',')
    |> parse ",a,,"
--> Ok [ "", "a", "", "" ]

-- An empty input text gives a single empty string element.
split (char ',')
    |> parse ""
--> Ok [ "" ]

splitIncluding : Parser a -> (String -> a) -> Parser (List a)

Splits the input text by a separator parser into a List of Strings. The separators cannot overlap, and are interleaved alongside the values in the order found.

import Parser exposing (map, parse)
import Parser.Common exposing (text)

type Token
    = Separator
    | Value String

-- Note that both values and separators must be of the same type.
splitIncluding (text "," |> map (\_ -> Separator)) Value
    |> parse "a,bc,def"
--> Ok [ Value "a", Separator, Value "bc", Separator, Value "def" ]

-- Leading/trailing separators are valid and give empty values.
splitIncluding (text "," |> map (\_ -> Separator)) Value
    |> parse ",a,,"
--> Ok [ Value "", Separator, Value "a", Separator, Value "", Separator, Value "" ]

-- An empty input text gives a single element from an empty string.
splitIncluding (text "," |> map (\_ -> Separator)) Value
    |> parse ""
--> Ok [ Value "" ]