davidcavazos / parser / Parser.Common

Common and other useful parsers.

int : Parser Basics.Int

Matches an integer value as an Int.

import Parser exposing (parse)
import Parser.Error

-- You can parse integers as `Int` instead of `String`.
parse "123" int --> Ok 123

-- It also works with negative numbers.
parse "-123" int --> Ok -123

-- A decimal number is _not_ an integer :)
parse "3.14" int
    |> Result.mapError Parser.Error.message
--> Err "1:2: I was expecting an integer value. I got stuck when I got the character '.'."

-- But not with invalid numbers.
parse "abc" int
    |> Result.mapError Parser.Error.message
--> Err "1:1: I was expecting an integer value. I got stuck when I got the character 'a'."

number : Parser Basics.Float

Matches a decimal value as a Float.

import Parser exposing (parse)
import Parser.Error

-- You can parse numbers as `Float` instead of `String`.
parse "12" number --> Ok 12.0
parse "12." number --> Ok 12.0
parse "12.34" number --> Ok 12.34
parse ".12" number --> Ok 0.12

-- It also works with negative numbers.
parse "-12.34" number --> Ok -12.34
parse "-.12" number --> Ok -0.12

-- But not with invalid numbers.
parse "." number
    |> Result.mapError Parser.Error.message
--> Err "1:1: I was expecting a digit [0-9]. I got stuck when I got the character '.'."

parse "abc" number
    |> Result.mapError Parser.Error.message
--> Err "1:1: I was expecting a digit [0-9]. I got stuck when I got the character 'a'."

text : String -> Parser String

Matches a specific text string. This is case sensitive.

import Parser exposing (parse)

-- Match an exact text, case sensitive.
parse "abcdef" (text "abc") --> Ok "abc"

-- But anything else makes it fail.
import Parser.Error

text "abc"
    |> parse "abCDEF"
    |> Result.mapError Parser.Error.message
--> Err "1:3: I was expecting the text 'abc'. I got stuck when I got the character 'C'."

textNoCase : String -> Parser String

Matches a specific text string. This is case insensitive.

import Parser exposing (parse)

-- Match an exact text, case insensitive.
parse "abcdef" (textNoCase "abc") --> Ok "abc"
parse "ABCDEF" (textNoCase "abc") --> Ok "ABC"

-- But anything else makes it fail.
import Parser.Error

textNoCase "abc"
    |> parse "ab@"
    |> Result.mapError Parser.Error.message
--> Err "1:3: I was expecting the text 'abc' (case insensitive). I got stuck when I got the character '@'."

digits : Parser String

Matches one or more digit.

ℹ️ Equivalent regular expression: [0-9]+ or \d+

import Parser exposing (parse)

-- Match many digits.
parse "123abc" digits --> Ok "123"

-- But anything else makes it fail.
import Parser.Error

digits
    |> parse "abc123"
    |> Result.mapError Parser.Error.message
--> Err "1:1: I was expecting one or more digits [0-9]+. I got stuck when I got the character 'a'."

letters : Parser String

Matches one or more letter. This is case insensitive.

ℹ️ Equivalent regular expression: [a-zA-Z]+

import Parser exposing (parse)

-- Match many letters, case insensitive.
parse "abc123" letters --> Ok "abc"
parse "ABC123" letters --> Ok "ABC"

-- But anything else makes it fail.
import Parser.Error

letters
    |> parse "123abc"
    |> Result.mapError Parser.Error.message
--> Err "1:1: I was expecting one or more letters [a-zA-Z]+. I got stuck when I got the character '1'."

spaces : Parser String

Matches zero or more Unicode blank spaces, including new lines.

ℹ️ Equivalent regular expression: [ \t\n\r\f]* or \s*

import Parser exposing (parse)

-- Match many spaces.
parse "    abc" spaces --> Ok "    "

parse "\n\t abc" spaces --> Ok "\n\t "

-- Including zero spaces :)
parse "abc" spaces --> Ok ""

line : Parser String

Matches a line from the input text, delimited by '\n'.

import Parser exposing (parse)
import Parser.Error
import Parser.Sequence exposing (zeroOrMore)

-- A line could be delimited by the newline character '\n'.
parse "abc\ndef" line --> Ok "abc"

-- Or this could also be the last line.
parse "abc" line --> Ok "abc"

-- An empty line still counts.
parse "\n" line --> Ok ""

-- But not an empty file.
line
    |> parse ""
    |> Result.mapError Parser.Error.message
--> Err "1:0: I was expecting a line. I reached the end of the input text."

-- So we can parse multiple lines.
zeroOrMore line
    |> parse "abc\ndef\nghi"
--> Ok [ "abc", "def", "ghi"]

token : Parser a -> Parser a

Matches a parser as a token that can be preceded and followed by zero or more spaces.

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

parse "1+2" (map3 (\x _ y -> x + y) int (token (char '+')) int) --> Ok 3
parse "1 + 2" (map3 (\x _ y -> x + y) int (token (char '+')) int) --> Ok 3
parse "1  +  2" (map3 (\x _ y -> x + y) int (token (char '+')) int) --> Ok 3