atto

Parser combinators for parsing arbitrary stream types.

Types

An expected or found component of an error.

pub type ErrorPart(t) {
  Token(t)
  Msg(String)
}

Constructors

  • Token(t)
  • Msg(String)

An error that occurred during parsing.

pub type ParseError(e, t) {
  ParseError(
    span: Span,
    got: ErrorPart(t),
    expected: Set(ErrorPart(t)),
  )
  Custom(span: Span, value: e)
}

Constructors

  • ParseError(
      span: Span,
      got: ErrorPart(t),
      expected: Set(ErrorPart(t)),
    )
  • Custom(span: Span, value: e)

Parser monad. This type is parameterised by the result a, the token t, the stream s, a custom context c, and the error type e.

pub type Parser(a, t, s, c, e) {
  Parser(
    run: fn(ParserInput(t, s), Pos, c) ->
      Result(#(a, ParserInput(t, s), Pos, c), ParseError(e, t)),
  )
}

Constructors

  • Parser(
      run: fn(ParserInput(t, s), Pos, c) ->
        Result(#(a, ParserInput(t, s), Pos, c), ParseError(e, t)),
    )

An input to the parser. This type is parameterised by the token type t and the token stream s.

pub type ParserInput(t, s) {
  ParserInput(
    src: s,
    get: fn(s, #(Int, Int)) -> Result(#(t, s, #(Int, Int)), Nil),
    render_token: fn(t) -> String,
    render_span: fn(s, Span) -> #(String, String, String),
  )
}

Constructors

  • ParserInput(
      src: s,
      get: fn(s, #(Int, Int)) -> Result(#(t, s, #(Int, Int)), Nil),
      render_token: fn(t) -> String,
      render_span: fn(s, Span) -> #(String, String, String),
    )

    Arguments

    • src

      The token stream being consumed.

    • get

      Get the next token from the input stream, returning the token, the new stream and the new line/column, or an error on EOF.

    • render_token

      Produce a string representation of a token.

    • render_span

      Given the original stream and a span, render the span section of the stream and the before and after context. This is used for error messages. Usually, this will return the line that an error occurred on, split around the span.

      Examples

      let in = text.new("foo bar baz")
      let sp = Span(Pos(1, 5), Pos(1, 8))
      in.render(in.src, sp)
      // -> #("foo ", "bar", "  baz")
      

A position in the input stream, with an index, line, and column.

pub type Pos {
  Pos(idx: Int, line: Int, col: Int)
}

Constructors

  • Pos(idx: Int, line: Int, col: Int)

A span of positions in the input stream.

pub type Span {
  Span(start: Pos, end: Pos)
}

Constructors

  • Span(start: Pos, end: Pos)

Functions

pub fn any() -> Parser(a, a, b, c, d)

Parse any token.

pub fn ctx() -> Parser(a, b, c, a, d)

Get the current context value.

pub fn ctx_put(
  f: fn(a) -> a,
  p: fn() -> Parser(b, c, d, a, e),
) -> Parser(b, c, d, a, e)

Modify the current context within a parser.

Examples

{
  use <- ctx_put(fn(x) { x + 1 })
  usw x <- ctx()
  pure(x)
}
|> run(text.new(""), 5)
// -> Ok(6)
pub fn do(
  p: Parser(a, b, c, d, e),
  f: fn(a) -> Parser(f, b, c, d, e),
) -> Parser(f, b, c, d, e)

Compose two parsers. This can be used with use syntax.

Examples

{
  use a <- do(token("a"))
  use b <- do(token("b"))
  pure(#(a, b))
}
|> run(text.new("ab"), Nil)
// -> Ok(("a", "b"))
pub fn drop(
  p: Parser(a, b, c, d, e),
  q: fn() -> Parser(f, b, c, d, e),
) -> Parser(f, b, c, d, e)

Compose two parsers, discarding the result of the first. This is just a wrapper for do for convenient syntax with use.

Examples

use <- drop(token("a"))
pub fn eof() -> Parser(Nil, a, b, c, d)

Matches the end of input. This should be placed at the end of the parser chain to ensure that the entire input is consumed.

pub fn fail(error: a) -> Parser(b, c, d, e, a)

Fail with a custom error value.

pub fn fail_msg(msg: String) -> Parser(a, b, c, d, e)

Fail with a message.

pub fn get_token(
  in: ParserInput(a, b),
  pos: Pos,
) -> Result(#(a, ParserInput(a, b), Pos), ParseError(c, a))

Get the next token from the input stream.

Examples

get(text.new("foo"), Pos(1, 1))
// -> Ok(#("f", text.new("oo"), Pos(1, 2))
pub fn label(
  name: String,
  f: fn() -> Parser(a, b, c, d, e),
) -> Parser(a, b, c, d, e)

Label a parser. When this parser fails without consuming input, the ‘expected’ message is set to this message.

Examples

{
  use <- label("foo")
  satisfy(fn(c) { c == "5" })
}
|> run(text.new("6"), Nil)
// -> Error(ParseError(Pos(1, 1), Token("6"), set.insert(set.new(), Msg("foo")))
pub fn map(
  p: Parser(a, b, c, d, e),
  f: fn(a) -> f,
) -> Parser(f, b, c, d, e)

Map a function over the result of a parser.

Examples

pure(5) |> map(fn(x) { x + 1 })
|> run(text.new(""), Nil)
// -> Ok(6)
fail("oops!") |> map(fn(x) { x + 1 })
|> run(text.new(""), 5)
// -> Error(Custom(Pos(1, 1), "oops!"))
pub fn pos() -> Parser(Pos, a, b, c, d)

Get the current position in the input stream.

Examples

{
  use <- drop(token("a"))
  use p1 <- do(pos())
  use <- drop(token("b"))
  use p2 <- do(pos())
  pure(#(p1, p2))
}
|> run(text.new("ab"), Nil)
// -> Ok((Pos(1, 2), Pos(1, 3)))
pub fn pure(value: a) -> Parser(a, b, c, d, e)

Lift a value into the parser context.

pub fn recover(
  p: Parser(a, b, c, d, e),
  in: ParserInput(b, c),
  pos: Pos,
  ctx: d,
  f: fn(ParseError(e, b)) ->
    Result(#(a, ParserInput(b, c), Pos, d), ParseError(e, b)),
) -> Result(#(a, ParserInput(b, c), Pos, d), ParseError(e, b))

Try to run a parser. When it fails without consuming input, run the provided function.

pub fn run(
  p: Parser(a, b, c, d, e),
  in: ParserInput(b, c),
  ctx: d,
) -> Result(a, ParseError(e, b))

Run a parser against an input stream, returning a result or an error.

Examples

ops.many(ops.choice([token("a"), token("b")]))
|> run(text.new("aaba"), Nil)
// -> Ok(["a", "a", "b", "a"])
pub fn satisfy(f: fn(a) -> Bool) -> Parser(a, a, b, c, d)

Parse a token if it matches a predicate. This should be labelled with label to provide a useful error message.

Examples

satisfy(fn(c) { c == "5" })
|> run(text.new("5"), Nil)
// -> Ok("5")
{
  use <- label("digit")
  satisfy(fn(c) { "0" <= c && c <= "9" })
}
|> run(text.new("a"), Nil)
// -> Error(ParseError(Pos(1, 1), Token("a"), set.insert(set.new(), Msg("digit")))
pub fn token(token: a) -> Parser(a, a, b, c, d)

Parse a specific token. This is a convenience wrapper around satisfy.

Search Document