Sign in

    nom

    Nom-like parser combinators for MoonBit.

    parser
    combinator
    nom
    Download zip
    Author
    Version
    0.1.3
    License
    Apache-2.0
    Last updated
    5 months ago
    Downloads
    40

    #nom for MoonBit

    Nom-like parser combinators for MoonBit. The core types mirror nom's IResult/Parser style while keeping MoonBit syntax and StringView/BytesView inputs.

    • IResult[I, O] = Result[(O, I), Err[ParseError[I]]]
    • Parser[I, O] = (I) -> IResult[I, O]

    #Quick example (string)

    ///| Example: parse an integer with optional whitespace

    fn parse_int_ws(input : StringView) -> @nom.IResult[StringView, Int] {
    let parser = @nom.delimited(
    @nom/str.space0,
    @nom/str.int,
    @nom/str.space0,
    )
    parser(input)
    }

    ///|
    test "parse int with ws" {
    let input = " 123 "[:]
    match parse_int_ws(input) {
    Ok((value, rest)) => {
    assert_eq(value, 123)
    assert_true(rest.is_empty())
    }
    Err(_) => fail("parse failed")
    }
    }

    #Packages

    • @nom - core combinators and error types
    • @nom/str - string parsers (StringView)
    • @nom/bytes - bytes parsers (BytesView)

    Streaming is partially supported: parsers may return Err::Incomplete(Needed::Size(_)) when the input is too short. Use @nom/str.Stream or @nom/bytes.Stream to buffer chunks and retry the parser, or use @nom.complete(parser) to treat Incomplete as a normal error for non-streaming parsing.

    ///| Example: streaming string buffer
    let parser = @nom/str.tag("abc"[:])
    let stream0 = @nom/str.Stream::new()
    let stream1 = stream0.feed("a"[:])
    let (res1, stream1b) = stream1.parse(parser)
    // res1 is Err::Incomplete(_)
    let stream2 = stream1b.feed("bc"[:])
    let (res2, stream3) = stream2.parse(parser)
    // res2 is Ok(("abc", "")) and stream3 is now empty

    #Benchmark vs Rust nom (local)

    Numbers below are local microbenchmarks run on 2026-02-02. They are sensitive to machine and compiler versions, so treat them as rough guidance rather than absolute truth.

    • MoonBit: moon bench --target native
    • Rust: cargo bench --bench calculator and cargo bench --bench assignments_unicode

    caseMoonBit (µs)Rust/nom (µs)ratio
    calc short0.660.2592.55x
    calc long3.821.7182.22x
    calc complex short1.590.8151.95x
    calc complex long4.072.1451.90x
    assignments unicode short0.900.5911.52x
    assignments unicode long3.712.6171.42x

    Short, ASCII-heavy inputs tend to amplify constant overhead. As expressions get longer or more Unicode-heavy, the gap shrinks.

    IResult

    type IResult[I, O] = Result[(O, I), Err[ParseError[I]]]
    Nom-style result type

    Parser

    type Parser[I, O] = (I) -> Result[(O, I), Err[ParseError[I]]]
    Parser function type

    InputLen

    pub trait InputLen {
    length(Self) -> Int
    }
    Input length trait for parsers that need to detect progress

    Err

    pub(all) enum Err[E] {
    Error(E)
    Failure(E)
    Incomplete(Needed)
    } derive(Eq, Show, ToJson)
    Error wrapper (recoverable vs failure)

    ErrorKind

    pub(all) enum ErrorKind {
    Tag
    Take
    TakeWhile
    TakeWhile1
    TakeUntil1
    Char
    Digit
    Eof
    MapRes
    Alt
    Many0
    Custom(String)
    } derive(Eq, Show, ToJson)
    Error kinds similar to nom::error::ErrorKind

    Needed

    pub(all) enum Needed {
    Unknown
    Size(Int)
    } derive(Eq, Show, ToJson)
    Streaming-needed hint (like nom::Needed)

    ParseError

    pub struct ParseError[I] {
    input : I
    kind : ErrorKind
    context : Array[String]
    } derive(Eq, Show, ToJson)
    Parse error with input snapshot and optional context stack

    ParseError::new

    fn[I] ParseError::new(input : I, kind : ErrorKind) -> ParseError[I]

    ParseError::with_context

    fn[I] ParseError::with_context(self : ParseError[I], label : String) -> ParseError[I]

    all_consuming

    fn[I : InputLen, O] all_consuming(parser : (I) -> Result[(O, I), Err[ParseError[I]]]) -> ((I) -> Result[(O, I), Err[ParseError[I]]])
    Ensure the parser consumes all input

    alt

    fn[I, O] alt(parsers : Array[(I) -> Result[(O, I), Err[ParseError[I]]]]) -> ((I) -> Result[(O, I), Err[ParseError[I]]])
    Try parsers in order, returning the first success

    alt2

    fn[I, O] alt2(first : (I) -> Result[(O, I), Err[ParseError[I]]], second : (I) -> Result[(O, I), Err[ParseError[I]]]) -> ((I) -> Result[(O, I), Err[ParseError[I]]])
    Try two parsers in order, returning the first success

    bytes_error_offset

    fn bytes_error_offset(original : BytesView, err : ParseError[BytesView]) -> Int
    Byte offset of a parsing error for BytesView inputs

    complete

    fn[I, O] complete(parser : (I) -> Result[(O, I), Err[ParseError[I]]]) -> ((I) -> Result[(O, I), Err[ParseError[I]]])
    Convert streaming Incomplete into a normal error (for complete parsing)

    context

    fn[I, O] context(label : String, parser : (I) -> Result[(O, I), Err[ParseError[I]]]) -> ((I) -> Result[(O, I), Err[ParseError[I]]])
    Attach context labels to errors

    cut

    fn[I, O] cut(parser : (I) -> Result[(O, I), Err[ParseError[I]]]) -> ((I) -> Result[(O, I), Err[ParseError[I]]])
    Convert recoverable errors into failures (like nom::combinator::cut)

    delimited

    fn[I, O1, O2, O3] delimited(open : (I) -> Result[(O1, I), Err[ParseError[I]]], inner : (I) -> Result[(O2, I), Err[ParseError[I]]], close : (I) -> Result[(O3, I), Err[ParseError[I]]]) -> ((I) -> Result[(O2, I), Err[ParseError[I]]])
    Parse with start/end delimiters

    eof

    fn[I : InputLen] eof(input : I) -> Result[(Unit, I), Err[ParseError[I]]]
    Match end of input

    fold_many0

    fn[I : InputLen, O, R] fold_many0(parser : (I) -> Result[(O, I), Err[ParseError[I]]], init : () -> R, fold : (R, O) -> R) -> ((I) -> Result[(R, I), Err[ParseError[I]]])
    Parse zero or more, folding into an accumulator (no allocations)

    fold_many1

    fn[I : InputLen, O, R] fold_many1(parser : (I) -> Result[(O, I), Err[ParseError[I]]], init : (O) -> R, fold : (R, O) -> R) -> ((I) -> Result[(R, I), Err[ParseError[I]]])
    Parse one or more, folding into an accumulator (no allocations)

    format_error_bytes

    fn format_error_bytes(original : BytesView, err : Err[ParseError[BytesView]]) -> String
    Format a bytes parsing error with offset and context

    format_error_string

    fn format_error_string(original : StringView, err : Err[ParseError[StringView]]) -> String
    Format a string parsing error with location and context

    many0

    fn[I : InputLen, O] many0(parser : (I) -> Result[(O, I), Err[ParseError[I]]]) -> ((I) -> Result[(Array[O], I), Err[ParseError[I]]])
    Parse zero or more, collecting results

    many1

    fn[I : InputLen, O] many1(parser : (I) -> Result[(O, I), Err[ParseError[I]]]) -> ((I) -> Result[(Array[O], I), Err[ParseError[I]]])
    Parse one or more, collecting results

    map

    fn[I, O, O2] map(parser : (I) -> Result[(O, I), Err[ParseError[I]]], f : (O) -> O2) -> ((I) -> Result[(O2, I), Err[ParseError[I]]])
    Map the output of a parser

    map_err

    fn[I, O] map_err(parser : (I) -> Result[(O, I), Err[ParseError[I]]], f : (ParseError[I]) -> ParseError[I]) -> ((I) -> Result[(O, I), Err[ParseError[I]]])
    Map errors

    map_res

    fn[I, O, O2, E2] map_res(parser : (I) -> Result[(O, I), Err[ParseError[I]]], f : (O) -> Result[O2, E2], kind : ErrorKind) -> ((I) -> Result[(O2, I), Err[ParseError[I]]])
    Map the output with a fallible function

    opt

    fn[I, O] opt(parser : (I) -> Result[(O, I), Err[ParseError[I]]]) -> ((I) -> Result[(O?, I), Err[ParseError[I]]])
    Optional parser (never fails)

    pair

    fn[I, O1, O2] pair(first : (I) -> Result[(O1, I), Err[ParseError[I]]], second : (I) -> Result[(O2, I), Err[ParseError[I]]]) -> ((I) -> Result[((O1, O2), I), Err[ParseError[I]]])
    Parse two parsers in sequence

    preceded

    fn[I, O1, O2] preceded(first : (I) -> Result[(O1, I), Err[ParseError[I]]], second : (I) -> Result[(O2, I), Err[ParseError[I]]]) -> ((I) -> Result[(O2, I), Err[ParseError[I]]])
    Parse second after first, returning the second

    separated_list0

    fn[I : InputLen, O, S] separated_list0(sep : (I) -> Result[(S, I), Err[ParseError[I]]], parser : (I) -> Result[(O, I), Err[ParseError[I]]]) -> ((I) -> Result[(Array[O], I), Err[ParseError[I]]])
    Parse a separated list (zero or more)

    separated_list1

    fn[I : InputLen, O, S] separated_list1(sep : (I) -> Result[(S, I), Err[ParseError[I]]], parser : (I) -> Result[(O, I), Err[ParseError[I]]]) -> ((I) -> Result[(Array[O], I), Err[ParseError[I]]])
    Parse a separated list (one or more)

    string_error_location

    fn string_error_location(original : StringView, err : ParseError[StringView]) -> (Int, Int, Int)
    Return (offset, line, column) for a StringView parsing error

    string_error_offset

    fn string_error_offset(original : StringView, err : ParseError[StringView]) -> Int
    Code-unit offset of a parsing error for StringView inputs

    terminated

    fn[I, O1, O2] terminated(first : (I) -> Result[(O1, I), Err[ParseError[I]]], second : (I) -> Result[(O2, I), Err[ParseError[I]]]) -> ((I) -> Result[(O1, I), Err[ParseError[I]]])
    Parse second after first, returning the first

    to_error

    fn[I] to_error(input : I, kind : ErrorKind) -> Err[ParseError[I]]

    to_failure

    fn[I] to_failure(input : I, kind : ErrorKind) -> Err[ParseError[I]]

    to_incomplete

    fn[E] to_incomplete(needed : Needed) -> Err[E]

    value

    fn[I, O, O2] value(v : O2, parser : (I) -> Result[(O, I), Err[ParseError[I]]]) -> ((I) -> Result[(O2, I), Err[ParseError[I]]])
    Map the output, returning a constant value on success