Extending the Library
This document provides checklists for adding new AST nodes, token types, and supported
serialisation/deserialisation types to Huml.Net. All extension work is internal — the public API
surface is the static Huml facade in src/Huml.Net/Huml.cs and the AST node hierarchy; no
other pipeline classes are exposed to consumers.
Adding a New AST Node
Create the new record in
src/Huml.Net/Parser/— one file per type. The Meziantou MA0048 analyser rule is active underTreatWarningsAsErrors, so multiple types in one file will cause a build failure:public sealed record MyNode(...) : HumlNode;Inherit from
HumlNodedirectly (the abstract base record). Do not extendHumlDocumentor any other concrete node type.Update the parser production sites in
src/Huml.Net/Parser/HumlParser.csto construct and return the new node where the grammar requires it.Update
HumlDeserializer.DeserializeNode()insrc/Huml.Net/Serialization/HumlDeserializer.cs— add anif (node is MyNode ...)branch so the deserialiser knows how to map the new node to .NET objects.Update
HumlSerializer.SerializeValue()insrc/Huml.Net/Serialization/HumlSerializer.csif the new node has a serialiser counterpart (i.e. if a .NET type should round-trip throughMyNode).Add unit tests covering the new node: parser tests that assert the node is produced for the correct input, deserialiser tests that assert the correct .NET value is returned, and serialiser tests if applicable.
Worked example: HumlInlineMapping (added in Phase 07.2)
// src/Huml.Net/Parser/HumlInlineMapping.cs
public sealed record HumlInlineMapping(IReadOnlyList<HumlNode> Entries) : HumlNode;
HumlInlineMapping represents an inline {key: value} block. It extends HumlNode directly
rather than HumlDocument so that deserialiser dispatch can distinguish inline mapping blocks
from root and nested multiline mapping blocks (which both return HumlDocument). Three dispatch
sites were updated when it was introduced: the parser (HumlParser.cs), the deserialiser
(HumlDeserializer.cs), and the test assertions.
Adding a New Token Type
Add a new member to the
TokenTypeenum insrc/Huml.Net/Lexer/TokenType.cs.Update
src/Huml.Net/Lexer/Lexer.cs— either add a dedicatedScan*method for the new token or extend the dispatch inNextToken()to emit the newTokenTypefor the appropriate input characters.Update
src/Huml.Net/Parser/HumlParser.csto consume the new token. Add production methods or extend existing ones so the Parser correctly handles the new token in the grammar.Add Lexer unit tests in
tests/Huml.Net.Tests/Lexer/LexerTests.csto confirm the new token is emitted for the correct input.Add Parser unit tests in
tests/Huml.Net.Tests/Parser/HumlParserTests.csto confirm the parser handles the new token correctly.
Current TokenType members for reference (18 members):
| Group | Members |
|---|---|
| Structural | Eof, Error |
| Directive | Version |
| Keys | Key, QuotedKey |
| Indicators | ScalarIndicator, VectorIndicator, ListItem, Comma |
| Scalars | String, Int, Float, Bool, Null, NaN, Inf |
| Empty collections | EmptyList, EmptyDict |
Adding a New Supported Type
This covers adding support for serialising or deserialising a .NET type that Huml.Net does not
currently handle (e.g. DateTimeOffset, Guid, or a new numeric type).
Update the type dispatch in
HumlSerializer.SerializeValue()insrc/Huml.Net/Serialization/HumlSerializer.cs. Add the new type check before the final POCO fallback. Note:stringmust remain before theIEnumerable<T>check —stringimplementsIEnumerable<char>and would otherwise be serialised as a character sequence.Update
HumlDeserializer.CoerceScalar()for scalar types (e.g. a type that maps directly to a HUML scalar literal), orHumlDeserializer.DeserializeNode()for structured types (e.g. a type that maps to aHumlDocumentorHumlSequencenode). File:src/Huml.Net/Serialization/HumlDeserializer.csNote: for truly custom type mapping, prefer implementing
HumlConverter<T>rather than modifying the deserialiser directly — seedocs/custom-converters.md.Update consumer-facing type mapping documentation if a
docs/serialisation.mdguide exists.Add round-trip tests in
tests/Huml.Net.Tests/HumlStaticApiTests.csor a new test file. A round-trip test serialises a .NET value to HUML text and then deserialises it back, asserting value equality.
Key Conventions
internal sealedfor pipeline classes. The Lexer, Parser, Serialiser, and Deserialiser are allinternal sealed. Consumers never access them directly. New pipeline helpers must follow the same visibility rule.public sealed recordfor AST nodes. Records provide structural equality and immutability by default. Do not useclassfor new AST node types.No external runtime dependencies. Any new code must use only BCL types. Adding an external package to
src/Huml.Net/Huml.Net.csprojis not permitted.One type per file. The Meziantou MA0048 analyser rule is enforced under
TreatWarningsAsErrors. Placing multiple types in a single file will fail the build.TreatWarningsAsErrorsis active across all TFMs. New code must compile cleanly onnetstandard2.1,net8.0,net9.0, andnet10.0with zero warnings.British English spelling in all documentation, comments, and string literals visible to contributors. Use
serialisation,deserialisation,behaviour,recognised— not their American equivalents.