data v0.5.1 Data.Parser

Higher-order functions to create and modify parsers.

Link to this section Summary

Types

A parser is a function that takes any value as input and produces a Result.t.

Functions

Takes a parser p and creates a parser that will successfully parse lists of values that all satisfy p.

Takes a parser and transforms it so that it works 'inside' Maybe.t values.

Creates a parser that behaves exactly the same as the list/1 parser, except that it will return the domain error :empty_list if applied to an empty list.

Takes a list of values, elements, and returns a parser that returns successfully if its input is present in elements.

Takes a boolean function p (the predicate), and returns a parser that parses successfully those values for which p is true.

Takes a boolean function p (the predicate) and a default value, and returns a parser that parses successfully those values for which p is true.

Takes a parser p and creates a parser that will successfully parse sets of values that all satisfy p.

Takes a list of parsers and creates a parser that returns the first successful parse result, or an error listing the parsers and the failed input.

Link to this section Types

Link to this type

t(a, b)

t(a, b) :: (any() -> FE.Result.t(a, b))

A parser is a function that takes any value as input and produces a Result.t.

More specifically, a parser(a,b) is a fuction that takes any input and returns {:ok, a} on a successful parse or {:error, b} if parsing failed.

Link to this section Functions

See Data.Parser.KV.new/1.

Link to this function

list(p)

list(t(a, Error.t())) :: t([a], Error.t())

Takes a parser p and creates a parser that will successfully parse lists of values that all satisfy p.

Specifically, the input:

1) Must be a list

2) p must parse successfully all elements in the input

If this is the case, the output will be {:ok, list_of_parsed_values}.

If not all values can be parsed with p, the result will be the original parse error, enriched with the field :failed_element in the error details.

If the input is not a list, the domain error :not_a_list will be returned.

Examples

iex> Data.Parser.list(Data.Parser.BuiltIn.integer()).([])
{:ok, []}

iex> Data.Parser.list(Data.Parser.BuiltIn.integer()).([1,2,3])
{:ok, [1, 2, 3]}

iex> {:error, e} = Data.Parser.list(Data.Parser.BuiltIn.integer()).(%{a: :b})
...> Error.reason(e)
:not_a_list

iex> {:error, e} = Data.Parser.list(Data.Parser.BuiltIn.integer()).([1, :b, 3])
...> Error.reason(e)
:not_an_integer
...> Error.details(e)
%{failed_element: :b}
Link to this function

maybe(parser)

maybe(t(a, b)) :: t(FE.Maybe.t(a), FE.Maybe.t(b))

Takes a parser and transforms it so that it works 'inside' Maybe.t values.

If the original parser works on String.t(), the new one will work on Maybe.t(String.t()).

Successful parses on just() values return {:ok, {:just, result_of_parse}}. Unsuccessful parses on just() values reutrn {:error, parse_error}.

The parser will successfully return {:ok, :nothing} when applied to :nothing.

Examples

iex(2)> Data.Parser.maybe(
...> Data.Parser.predicate( &String.valid?/1, :invalid)).({:just, "good"})
{:ok, {:just, "good"}}

iex> Data.Parser.maybe(
...> Data.Parser.predicate( &String.valid?/1, :invalid)).({:just, 'bad'})
{:error, :invalid}

iex> Data.Parser.maybe(
...> Data.Parser.predicate( &String.valid?/1, :invalid)).(:nothing)
{:ok, :nothing}
Link to this function

nonempty_list(p)

nonempty_list(t(a, Error.t())) :: t([a, ...], Error.t())

Creates a parser that behaves exactly the same as the list/1 parser, except that it will return the domain error :empty_list if applied to an empty list.

Examples

iex> Data.Parser.nonempty_list(Data.Parser.BuiltIn.integer()).([1, 2, 3])
{:ok, [1, 2, 3]}

iex> {:error, e} = Data.Parser.nonempty_list(Data.Parser.BuiltIn.integer()).([1, :b, 3])
...> Error.reason(e)
:not_an_integer
...> Error.details(e)
 %{failed_element: :b}


iex> {:error, e} = Data.Parser.nonempty_list(Data.Parser.BuiltIn.integer()).([])
...> Error.reason(e)
:empty_list
Link to this function

one_of(elements, default)

one_of([a], b | (a -> b)) :: t(a, b) when a: var, b: var

Takes a list of values, elements, and returns a parser that returns successfully if its input is present in elements.

If the input is not a member of elements and default is a value, the parser fails with {:error, default}. If default is a unary function, the parser fails with {:error, default.(input)}.

Examples

iex> Data.Parser.one_of([:he, :ne, :ar, :kr, :xe, :rn], "not a noble gas").(:he)
{:ok, :he}

iex> Data.Parser.one_of([:he, :ne, :ar, :kr, :xe, :rn], "not a noble gas").(:n)
{:error, "not a noble gas"}

iex> Data.Parser.one_of([:he, :ne, :ar, :kr, :xe, :rn],
...> fn x -> "not a noble gas: #{inspect x}" end).(:o)
{:error, "not a noble gas: :o"}
Link to this function

predicate(p)

predicate((a -> boolean())) :: t(a, Error.t()) when a: var

Takes a boolean function p (the predicate), and returns a parser that parses successfully those values for which p is true.

If the predicate returns false the parser will return a domain Error with the input value and the predicate functions listed in the error details.

Examples

iex> {:error, e} = Data.Parser.predicate(&String.valid?/1).('charlists are not ok')
...> e.reason
:predicate_not_satisfied
...> e.details
%{input: 'charlists are not ok', predicate: &String.valid?/1}

iex> Data.Parser.predicate(&String.valid?/1).("this is fine")
{:ok, "this is fine"}

iex> Data.Parser.predicate(&(&1<10)).(5)
{:ok, 5}

iex> {:error, e} = Data.Parser.predicate(&(&1<10)).(55)
...> e.details.input
55
Link to this function

predicate(p, default)

predicate((a -> boolean()), b | (a -> b)) :: t(a, b) when a: var, b: var

Takes a boolean function p (the predicate) and a default value, and returns a parser that parses successfully those values for which p is true.

If the predicate function applied to the input returns true, the parser wraps the input in an {:ok, input} tuple.

If the predicate function returns false, and default is a value, the parser returns {:error, default}

If the predicate returns false and default is a unary function, the parser returns {:error, default.(the_failed_input)}.

Examples

iex> Data.Parser.predicate(&String.valid?/1, "invalid string").('charlists are not ok')
{:error, "invalid string"}

iex> Data.Parser.predicate(&String.valid?/1, "invalid string").(<<"neither are invalid utf sequences", 99999>>)
{:error, "invalid string"}

iex> Data.Parser.predicate(&String.valid?/1, "invalid string").("this is fine")
{:ok, "this is fine"}

iex> Data.Parser.predicate(&String.valid?/1, fn x -> "the bad value is: #{inspect x}" end).(12345)
{:error, "the bad value is: 12345"}
Link to this function

set(p)

set(t(a, Error.t())) :: t(MapSet.t(a), Error.t())

Takes a parser p and creates a parser that will successfully parse sets of values that all satisfy p.

Specifically, the input:

1) must be a MapSet

2) all elements of the input set must be parsed correctly by p

If this is the case, the output will be {:ok, set_of_parsed_values}.

If not all values can be parsed with p, the result will be the original parse error, enriched with the field :failed_element in the error details.

If the input is not a MapSet, the domain error :not_a_set will be returned.

Examples

iex> {:ok, s} = Data.Parser.set(Data.Parser.BuiltIn.integer()).(MapSet.new())
...> s
#MapSet<[]>

iex> {:ok, s} = Data.Parser.set(Data.Parser.BuiltIn.integer()).(MapSet.new([1,2,3]))
...> s
#MapSet<[1, 2, 3]>

iex> {:error, e} = Data.Parser.set(Data.Parser.BuiltIn.integer()).(%{a: :b})
...> Error.reason(e)
:not_a_set

iex> {:error, e} = Data.Parser.set(Data.Parser.BuiltIn.integer()).(MapSet.new([1, :b, 3]))
...> Error.reason(e)
:not_an_integer
...> Error.details(e)
%{failed_element: :b}
Link to this function

union(parsers)

union([t(any(), any())]) :: t(any(), any())

Takes a list of parsers and creates a parser that returns the first successful parse result, or an error listing the parsers and the failed input.

Examples

iex> Data.Parser.union(
...> [Data.Parser.BuiltIn.integer(),
...>  Data.Parser.BuiltIn.boolean()]).(true)
{:ok, true}

iex> Data.Parser.union(
...> [Data.Parser.BuiltIn.integer(),
...>  Data.Parser.BuiltIn.boolean()]).(1)
{:ok, 1}

iex> {:error, e} = Data.Parser.union(
...>   [Data.Parser.BuiltIn.integer(),
...>    Data.Parser.BuiltIn.boolean()]).(:atom)
...> Error.reason(e)
:no_parser_applies
...> Error.details(e).input
:atom