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)
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)
Values
pub fn ctx_put(
f: fn(c) -> c,
p: fn() -> Parser(a, t, s, c, e),
) -> Parser(a, t, s, c, 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, t, s, c, e),
f: fn(a) -> Parser(b, t, s, c, e),
) -> Parser(b, t, s, c, 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, t, s, c, e),
q: fn() -> Parser(b, t, s, c, e),
) -> Parser(b, t, s, c, 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, t, s, c, e)
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 get_token(
in: ParserInput(t, s),
pos: Pos,
) -> Result(#(t, ParserInput(t, s), Pos), ParseError(e, t))
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, t, s, c, e),
) -> Parser(a, t, s, c, 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, t, s, c, e),
f: fn(a) -> b,
) -> Parser(b, t, s, c, 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, t, s, c, e)
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 recover(
p: Parser(a, t, s, c, e),
in: ParserInput(t, s),
pos: Pos,
ctx: c,
f: fn(ParseError(e, t)) -> Result(
#(a, ParserInput(t, s), Pos, c),
ParseError(e, t),
),
) -> Result(#(a, ParserInput(t, s), Pos, c), ParseError(e, t))
Try to run a parser. When it fails without consuming input, run the provided function.
pub fn run(
p: Parser(a, t, s, c, e),
in: ParserInput(t, s),
ctx: c,
) -> Result(a, ParseError(e, t))
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(t) -> Bool) -> Parser(t, t, s, c, e)
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: t) -> Parser(t, t, s, c, e)
Parse a specific token.
This is a convenience wrapper around satisfy.
pub fn try(
p: Parser(a, t, s, c, e),
) -> Parser(option.Option(a), t, s, c, e)
Try to run a parser. If it fails, backtrack.