#@parser

    HTML5 document and fragment parser. Builds a DOM tree from a string or a byte slice, with implicit html/head/body scaffolding, error recovery for malformed input, table foster-parenting, foreign-content handling, and structured parse diagnostics.

    Most callers reach the parser through the crate-root convenience wrappers (@html_parser.parse, @html_parser.parse_fragment, @html_parser.parse_bytes). This package is what those forward to.

    The examples below are mbt check blocks and run as part of moon test parser.

    #A first document parse

    parse always returns a ParsedHtml { root, errors, encoding }. The root is a real DOM tree with implicit <html> and <body> inserted even when the input only contains a body fragment.

    ///|
    test "readme parse hello" {
    let doc = @parser.parse("<p>hi</p>")
    inspect(
    @ser.to_html(doc.root, pretty=true),
    content=(
    #|<html>
    #| <head></head>
    #| <body>
    #| <p>hi</p>
    #| </body>
    #|</html>
    ),
    )
    }

    #Head vs body routing

    <meta>, <title>, <style>, and <script> get routed into the implicit <head>; everything else falls into the <body>.

    ///|
    test "readme parse head routing" {
    let doc = @parser.parse("<meta charset=utf-8><title>T</title><p>x</p>")
    inspect(
    @ser.to_html(doc.root, pretty=true),
    content=(
    #|<html>
    #| <head>
    #| <meta charset="utf-8">
    #| <title>T</title>
    #| </head>
    #| <body>
    #| <p>x</p>
    #| </body>
    #|</html>
    ),
    )
    }

    #Parsing a fragment instead of a document

    parse_fragment is the right entry point for snippets that should not be wrapped in implicit scaffolding — e.g. a <p> you intend to inject into an existing page.

    ///|
    test "readme parse_fragment basic" {
    let doc = @parser.parse_fragment("<p>hello <b>world</b></p>")
    inspect(
    @ser.to_html(doc.root, pretty=false),
    content=(
    #|<p>hello <b>world</b></p>
    ),
    )
    }

    Passing a FragmentContext tells the parser which container the fragment will live inside. In table context, bare <tr> is wrapped in an implicit <tbody> exactly like the document parser does.

    ///|

    ///|
    test "readme parse_fragment table context" {
    let doc = @parser.parse_fragment(
    "<tr><td>x</td></tr>",
    context=FragmentContext("table"),
    )
    inspect(
    @ser.to_html(doc.root, pretty=true),
    content=(
    #|<tbody>
    #| <tr>
    #| <td>x</td>
    #| </tr>
    #|</tbody>
    ),
    )
    }

    #Byte input with encoding detection

    parse_bytes runs BOM sniffing and meta-charset prescan and exposes the resolved encoding on the result. With no signal, naked high bytes fall back to windows-1252.

    ///|
    test "readme parse_bytes fallback" {
    let doc = @parser.parse_bytes(b"<p>\x80</p>")
    debug_inspect(
    (doc.encoding, @ser.to_html(doc.root, pretty=false)),
    content=(
    #|(
    #| Some("windows-1252"),
    #| "<html><head></head><body><p>€</p></body></html>",
    #|)
    ),
    )
    }

    #Collecting parse errors

    By default the parser recovers silently. collect_errors=true returns a structured diagnostic list with code, category, and source position.

    ///|
    test "readme parse collect_errors" {
    let doc = @parser.parse_fragment("<p>hi</span></p>", collect_errors=true)
    debug_inspect(
    doc.errors,
    content=(
    #|[
    #| {
    #| code: "unexpected-end-tag",
    #| line: Some(1),
    #| column: Some(12),
    #| category: "treebuilder",
    #| message: "unexpected-end-tag",
    #| },
    #|]
    ),
    )
    }

    #Querying and converting the parsed tree

    ParsedHtml carries convenience methods for the common post-processing steps: CSS-style queries, plain-text extraction, and Markdown rendering.

    ///|
    test "readme parsed query and to_markdown" {
    let doc = @parser.parse_fragment(
    "<h1>Title</h1><p class='lede'>Hello <b>MoonBit</b></p>",
    )
    inspect(
    doc.query(".lede").length(),
    content=(
    #|1
    ),
    )
    inspect(
    doc.to_markdown(),
    content=(
    #|# Title
    #|
    #|Hello **MoonBit**
    ),
    )
    }

    FragmentContext

    pub(all) struct FragmentContext {
    tag_name : String
    ns : String?
    } derive(Eq,
    Debug
    )

    Context element used when parsing an HTML fragment.

    The tag name is normalized to lowercase. ns can be used for foreign content contexts such as SVG or MathML.

    FragmentContext::FragmentContext

    fn FragmentContext::FragmentContext(tag_name : StringView, ns? : String) -> FragmentContext

    Construct a fragment parsing context.

    FragmentContext::equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn FragmentContext::equal(FragmentContext, FragmentContext) -> Bool

    FragmentContext::not_equal

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn FragmentContext::not_equal(x : FragmentContext, y : FragmentContext) -> Bool

    FragmentContext::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn FragmentContext::to_repr(FragmentContext) ->
    Repr

    ParsedHtml

    Result of parsing HTML.

    root is the document or fragment root. errors is populated when collect_errors=true or strict parsing observes an error. encoding is set by byte parsing APIs.

    ParsedHtml::query

    fn ParsedHtml::query(self : ParsedHtml, selector : StringView) -> Array[
    Node
    ]

    Return all descendants of the parsed root that match a CSS selector.

    ParsedHtml::query_one

    fn ParsedHtml::query_one(self : ParsedHtml, selector : StringView) ->
    Node
    ?

    Return the first descendant of the parsed root that matches a CSS selector.

    ParsedHtml::to_html

    fn ParsedHtml::to_html(self : ParsedHtml, pretty? : Bool, indent_size? : Int, context? :
    HtmlContext
    , quote? : Char) -> String raise
    HtmlError

    Serialize the parsed root node back to HTML.

    This forwards to to_html on self.root.

    ParsedHtml::to_markdown

    fn ParsedHtml::to_markdown(self : ParsedHtml, html_passthrough? : Bool) -> String raise
    HtmlError

    Render the parsed document or fragment root as Markdown.

    This is equivalent to calling to_markdown on self.root.

    ParsedHtml::to_repr

    #deprecated("implicit derived-impl promotion; call the trait method directly")
    fn ParsedHtml::to_repr(ParsedHtml) ->
    Repr

    ParsedHtml::to_text

    fn ParsedHtml::to_text(self : ParsedHtml, separator? : String, strip? : Bool, separator_blocks_only? : Bool) -> String

    Extract text from the parsed root node.

    This forwards to to_text on self.root.

    parse

    fn parse(html : StringView, sanitize? : Bool, collect_errors? : Bool, strict? : Bool, scripting_enabled? : Bool, xml_coercion? : Bool, track_node_locations? : Bool, iframe_srcdoc? : Bool) -> ParsedHtml raise
    HtmlError

    Parse a full HTML document from a string.

    The returned root is a document node with the usual html, head, and body scaffolding. Set sanitize=true to apply the default document sanitizer after parsing, collect_errors=true to keep parse diagnostics, and strict=true to raise @core.HtmlError::StrictMode on the first parse error.

    parse_bytes

    fn parse_bytes(input : BytesView, encoding? : String, sanitize? : Bool, collect_errors? : Bool, strict? : Bool, scripting_enabled? : Bool, xml_coercion? : Bool, track_node_locations? : Bool, iframe_srcdoc? : Bool) -> ParsedHtml raise
    HtmlError

    Decode and parse an HTML byte stream.

    When encoding is absent, BOMs and <meta charset> declarations are sniffed before falling back to Windows-1252. The detected or requested encoding is stored in ParsedHtml.encoding.

    parse_fragment

    fn parse_fragment(html : StringView, context? : FragmentContext, sanitize? : Bool, collect_errors? : Bool, strict? : Bool, scripting_enabled? : Bool, xml_coercion? : Bool, track_node_locations? : Bool, iframe_srcdoc? : Bool) -> ParsedHtml raise
    HtmlError

    Parse an HTML fragment from a string.

    context controls the fragment context element used by the tree builder. Without a context, the fragment is parsed into a generic fragment root. sanitize, collect_errors, strict, scripting_enabled, xml_coercion, and track_node_locations have the same meaning as in parse.