Current section

Files

Jump to
minipeg lib minipeg parser.ex
Raw

lib/minipeg/parser.ex

defmodule Minipeg.Parser do
use Minipeg.Types
alias Minipeg.{Cache, Error, Failure, Input, Success}
@moduledoc ~S"""
A struct containung a parser function and a name
"""
defstruct name: "",
parser_function: nil
@type t :: %__MODULE__{parser_function: parser_function_t(), name: binary()}
@type result_tuple_t(ast_t) :: {:ok, ast_t} | {:error, binary()}
@doc ~S"""
Create a new struct
"""
@spec new(binary(), parser_function_t()) :: t
def new(name, parser_function), do: %__MODULE__{name: name, parser_function: parser_function}
@doc ~S"""
Parses an input of type `Input.t` (and potentially a cache) with a given parser and returns a result of type `Success.t` or `Failure.t`
iex(1)> case parse(char_parser("a"), Input.new("abc")) do
...(1)> %Success{ast: ast, rest: %Input{input: rest}} -> {ast, rest}
...(1)> end
{"a", "bc"}
iex(2)> case parse(char_parser("a"), Input.new("")) do
...(2)> %Failure{reason: reason} -> reason
...(2)> end
"encountered end of input"
"""
@spec parse(t(), Input.t(), Cache.t()) :: result_t
def parse(%__MODULE__{parser_function: fun, name: name}, input, cache \\ %Cache{}) do
fun.(input, cache, name)
end
@doc ~S"""
A convenience function wrapping `parse` such as we can use a string as input and get and
`{:ok, ast} | {:error, reason}` tuple response
iex(3)> parse_string(char_parser("a"), "abc")
{:ok, "a"}
iex(4)> parse_string(char_parser("a"), "")
{:error, "encountered end of input (in char_parser(\"a\")) in <binary>:1,1"}
"""
@spec parse_string(t(), binary()) :: result_tuple_t(any())
def parse_string(%__MODULE__{parser_function: fun, name: name}, input_string) do
case fun.(Input.new(input_string), Cache.empty(), name) do
%Failure{} = f -> {:error, Failure.format(f)}
%Success{ast: ast} -> {:ok, ast}
end
end
@doc ~S"""
Another convenience function, unwrapping an `:ok` result of `parse_string` and
raising a `Minipeg.Error` otherwise
iex(5)> parse_string!(char_parser("a"), "abc")
"a"
iex(6)> assert_raise(Error, fn -> parse_string!(char_parser("a"), "") end)
"""
@spec parse_string!(t(), binary()) :: any()
def parse_string!(%__MODULE__{} = parser, input_string) do
case parse_string(parser, input_string) do
{:ok, result} -> result
{:error, reason} -> raise Error, reason
end
end
end
# SPDX-License-Identifier: Apache-2.0