Sign in

    vtty

    Zero-allocation, panic-free DEC VT500 terminal parser with UTF-8 decoding (MoonBit port)

    terminal
    vt100
    vt500
    ansi
    parser
    escape
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    11 hours ago
    Downloads
    1

    #vtty (MoonBit port)

    A byte-input DEC VT500 terminal parser with UTF-8 decoding — a port of the core vtty Rust crate to MoonBit (scalar path).

    #What is ported

    • DEC VT500 state machine (table.mbt) — all 14 parser states and the transition lookup, including the c1_transition / c1_in_sequence "anywhere" rule for 8-bit C1 controls.
    • Perform sink trait (perform.mbt) — print / print_bytes / print_codepoints / execute / csi_dispatch / esc_dispatch / hook / put / put_slice / unhook / osc_dispatch, all with no-op defaults.
    • Owned event API (event.mbt) — Event + Recording.
    • UTF-8 (utf8.mbt, scalar in parser.mbt) — W3C "Substitution of Maximal Subparts": each invalid maximal subpart becomes one U+FFFD, a standalone C1 byte keeps execute semantics, and code points split across advance calls are carried over as a partial.
    • Parser (parser.mbt) — zero-allocation dispatch to a Perform sink.
    • SIMD byte scans (simd.mbt) — first_non_printable / first_esc via @v128 (i8x16 comparisons + bitmask), plus branchless decode_validated. Opt in with Parser::with_simd().

    #Not ported (yet)

    • SIMD UTF-8 window validation (simdutf8 equivalent), vtty_emoji (ICU4X), vtty_ffi (C ABI), and the benchmark harness.

    #Usage

    let rec = @vtty.Recording::new()
    let p = @vtty.Parser::new() // or Parser::with_simd() for the SIMD fast path
    let input : Array[Int] = [0x1b, 0x5b, 0x31, 0x3b, 0x33, 0x31, 0x6d] // CSI 1;31m
    p.advance(rec, input[:])
    inspect(rec.events())

    #Terminal example

    cmd/term is an interactive minimal terminal emulator: it spawns a real shell on a PTY (via moonbit-community/pty), puts stdin into raw mode (via moonbit-community/tty), forwards keystrokes to the shell, and feeds the shell's output through the parser into an 80×24 screen sink (cursor tracking, CSI … H/A/B/C/D, ED, OSC title) that redraws on every chunk. Ctrl-D ends the session.

    moon run --target native cmd/term # interactive (native only) # or pipe a session: printf 'ls\nexit\n' | moon run --target native cmd/term

    #Test

    moon test moon check

    Perform

    pub(open) trait Perform {
    fn print(Self, Char) -> Unit = _
    fn print_bytes(Self, ArrayView[Int]) -> Unit = _
    fn print_codepoints(Self, ArrayView[Int]) -> Unit = _
    fn execute(Self, Int) -> Unit = _
    fn csi_dispatch(Self, ArrayView[Int], Int, ArrayView[Int], Int) -> Unit = _
    fn esc_dispatch(Self, ArrayView[Int], Int) -> Unit = _
    fn hook(Self, ArrayView[Int], ArrayView[Int], Int) -> Unit = _
    fn put(Self, Int) -> Unit = _
    fn put_slice(Self, ArrayView[Int]) -> Unit = _
    fn unhook(Self) -> Unit = _
    fn osc_dispatch(Self, Array[Array[Int]], Bool) -> Unit = _
    }

    default, so a terminal screen only overrides the events it cares about.

    Event

    pub(all) enum Event {
    Print(Char)
    Execute(Int)
    CsiDispatch(Array[Int], Array[Int], Int)
    EscDispatch(Array[Int], Int)
    DcsHook(Array[Int], Array[Int], Int)
    DcsPut(Int)
    DcsUnhook
    OscDispatch(Array[Array[Int]], Bool)
    } derive(Eq,
    Debug
    )

    Perform callback. Useful for differential testing and simple consumers.

    ParseState

    pub enum ParseState {
    Ground
    Escape
    EscapeIntermediate
    CsiEntry
    CsiParam
    CsiIntermediate
    CsiIgnore
    OscString
    DcsEntry
    DcsParam
    DcsIntermediate
    DcsPassthrough
    DcsIgnore
    SosPmApcString
    } derive(Eq,
    Debug
    )

    VT500 parser states (mutually exclusive).

    Parser

    pub struct Parser {
    state : ParseState
    params : Array[Int]
    params_len : Int
    param : Int
    param_empty : Bool
    sep : Int
    intermediates : Array[Int]
    intermediates_len : Int
    ignore : Bool
    partial : Int
    partial_need : Int
    osc : Array[Int]
    osc_starts : Array[Int]
    simd : Bool
    }

    Perform sink via advance; use Recording for the owned event API.

    Parser::advance

    fn[P : Perform] Parser::advance(self : Parser, p : P, bytes : ArrayView[Int]) -> Unit

    Parse bytes, dispatching each decoded event to the sink p.

    Parser::new

    fn Parser::new() -> Parser

    Create a parser in the ground state (scalar decode).

    Parser::with_simd

    fn Parser::with_simd() -> Parser

    Create a parser with the SIMD-accelerated printable-run scan enabled.

    Recording

    pub struct Recording {
    events : Array[Event]
    }

    API). Allocates one Event per dispatch — for tests and tooling.

    Recording::events

    fn Recording::events(self : Recording) -> Array[Event]

    The recorded events, in dispatch order.

    Recording::new

    fn Recording::new() -> Recording

    Create an empty recording.

    ACTION_CSI_DISPATCH

    let ACTION_CSI_DISPATCH : Int

    ACTION_ESC_DISPATCH

    let ACTION_ESC_DISPATCH : Int

    ACTION_EXECUTE

    let ACTION_EXECUTE : Int

    ACTION_HOOK

    let ACTION_HOOK : Int

    ACTION_NONE

    let ACTION_NONE : Int

    Action bit flags (a byte can trigger several).

    ACTION_OSC_END

    let ACTION_OSC_END : Int

    ACTION_OSC_PUT

    let ACTION_OSC_PUT : Int

    ACTION_OSC_START

    let ACTION_OSC_START : Int

    ACTION_PRINT

    let ACTION_PRINT : Int

    ACTION_PUT

    let ACTION_PUT : Int

    ACTION_UNHOOK

    let ACTION_UNHOOK : Int

    action_has

    fn action_has(action : Int, flag : Int) -> Bool

    Test whether action contains flag.

    c1_in_sequence

    fn c1_in_sequence(state : ParseState, b : Int) -> (Int, ParseState)

    terminate a string (ST 0x9c) or execute/re-enter the introducer.

    c1_transition

    fn c1_transition(b : Int) -> (Int, ParseState)

    return to ground.

    cp_to_char

    fn cp_to_char(cp : Int) -> Char

    U+FFFD.

    decode_validated

    fn decode_validated(b : ArrayView[Int], pos : Int) -> (Int, Int)

    Returns (code_point, byte_length).

    first_esc

    fn first_esc(bytes : ArrayView[Int]) -> Int

    Index of the first ESC byte (0x1b), or bytes.length() if none.

    first_non_printable

    fn first_non_printable(bytes : ArrayView[Int]) -> Int

    0x7f is a C0 control), or bytes.length() if every byte is printable.

    transition

    fn transition(state : ParseState, b : Int) -> (Int, ParseState)

    trigger several actions; the result is (action_bitmask, next_state).

    utf8_expected

    fn utf8_expected(lead : Int) -> Int

    Expected UTF-8 byte length for a lead byte (0 = invalid/continuation).

    utf8_lead_cp

    fn utf8_lead_cp(lead : Int) -> Int

    Code-point bits contributed by a UTF-8 lead byte.

    utf8_second_byte_valid

    fn utf8_second_byte_valid(lead : Int, second : Int) -> Bool

    (non-shortest / non-surrogate / non-overlong rules).