AST Usage Guide

HumlSerializer.Parse() returns a HumlDocument — the root of the abstract syntax tree (AST). Use the AST when you need to inspect or transform a HUML document without mapping it to a known .NET type.

Required Using Directives

using Huml.Net;
using Huml.Net.Parser;

Node Types

All AST nodes are public sealed record types in the Huml.Net.Parser namespace, all extending the abstract base HumlNode.

Type Constructor Role
HumlDocument HumlDocument(IReadOnlyList<HumlNode> Entries) Root of every parsed document
HumlMapping HumlMapping(string Key, HumlNode Value) A single key-value pair
HumlScalar HumlScalar(ScalarKind Kind, object? Value) A leaf value
HumlSequence HumlSequence(IReadOnlyList<HumlNode> Items) An ordered list
HumlInlineMapping HumlInlineMapping(IReadOnlyList<HumlNode> Entries) Inline or empty mapping block

ScalarKind Values

Kind Runtime Type of Value Example HUML
String string key: "hello"
Integer long key: 42
Float double key: 3.14
Bool bool key: true
Null null key: null
NaN string ("nan") key: nan
Inf string ("+inf" or "-inf") key: +inf

Note: NaN and Inf scalars carry the raw token string in Value rather than null, so that sign information (+inf vs -inf) is preserved for downstream consumers.

Basic Traversal

using Huml.Net;
using Huml.Net.Parser;

HumlDocument doc = HumlSerializer.Parse(humlString);

foreach (HumlNode entry in doc.Entries)
{
    if (entry is HumlMapping { Key: var key, Value: HumlScalar scalar })
    {
        Console.WriteLine($"{key} = {scalar.Value} ({scalar.Kind})");
    }
}

Pattern Matching with Nested Structures

Use recursive pattern matching to handle nested mappings and sequences:

foreach (HumlNode entry in doc.Entries)
{
    switch (entry)
    {
        case HumlMapping { Key: var key, Value: HumlScalar { Kind: ScalarKind.Integer, Value: long n } }:
            Console.WriteLine($"{key}: integer {n}");
            break;

        case HumlMapping { Key: var key, Value: HumlSequence seq }:
            Console.WriteLine($"{key}: sequence with {seq.Items.Count} items");
            break;

        case HumlMapping { Key: var key, Value: HumlDocument nested }:
            Console.WriteLine($"{key}: nested mapping block");
            // recurse into nested.Entries
            break;
    }
}

HumlInlineMapping

Inline mapping blocks (e.g. key:: a: 1, b: 2) and empty mapping blocks (key:: {}) produce HumlInlineMapping rather than HumlDocument. Both types expose an Entries property of type IReadOnlyList<HumlNode>, so walking code that handles both should match on either type:

case HumlMapping { Value: HumlDocument nested }:
    // process nested.Entries
    break;
case HumlMapping { Value: HumlInlineMapping inline }:
    // process inline.Entries
    break;

The distinction exists because HumlDocument is always the root node returned by HumlSerializer.Parse(), while HumlInlineMapping only appears as a value within a mapping.

Source Positions

All AST nodes carry the source position of the opening token in the HUML document.

Property Type Description
Line int 1-based line number, or 0 if position unknown
Column int 0-based column position, or 0 if unknown

These properties are excluded from structural equality — two nodes representing the same value at different positions are still considered equal by ==.

HumlDocument doc = HumlSerializer.Parse(humlString);

foreach (HumlNode entry in doc.Entries)
{
    if (entry is HumlMapping mapping)
    {
        Console.WriteLine($"Key '{mapping.Key}' at line {mapping.Line}, column {mapping.Column}");
    }
}

HumlDeserializeException uses the AST node position when reporting mapping failures, so the Line and Column properties on the exception reflect the source location of the problematic key.

Parsing Options

Pass HumlOptions.AutoDetect to read the spec version from the document header:

HumlDocument doc = HumlSerializer.Parse(humlString, HumlOptions.AutoDetect);

Or use the default options (v0.2, no header required):

HumlDocument doc = HumlSerializer.Parse(humlString);

See Options Reference for all available options.