Skip to content

holo-q/ronmamon

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ronmamon

Zero-allocation, AOT-clean UTF-8 RON (Rusty Object Notation) parser + source-generated binder for .NET.

ronmamon binds RON straight into your types with no reflection, no runtime codegen, and no per-document heap churn — a forward-only ref struct reader feeds a generated IRonFormatter<T> per [RonObject] type. It is built for NativeAOT and the kind of config/data layer where you want the file to read like the struct it deserializes into.

GameConfig(
    name: "bob",
    display: (
        scroll: ( friction: 0.78, spring: 96.0 ),
        tab: ( width: 2 ),
    ),
    servers: [
        ( id: "alpha", port: 8080 ),
        ( id: "beta",  port: 9090 ),
    ],
    mode: Dark,                 // enums are bare identifiers
)

Why RON (and why this is small)

RON nests with delimited values — ( … ) structs, [ … ] lists, { … } maps, each with an explicit close token. That single property is the whole point: a nested value is read recursively and returned, then assigned to its field. So binding is pure recursive value-descent — there is no header state machine, no section cursor, no write-back, and struct and class targets generate the identical shape. Deeply-nested config that would be a wall of [a.b.c] headers in TOML is just natural nesting in RON.

[RonObject]
public partial struct GameConfig
{
    public string name = "";
    public DisplayConfig display = new();
    public List<Server> servers = new();
    public Mode mode = Mode.Light;
    public GameConfig() { }
}

var cfg = RonSerializer.Deserialize<GameConfig>(File.ReadAllBytes("game.ron"));
string ron = RonSerializer.Serialize(cfg);   // readable, indented, round-trips

Features

  • Reflection-free + NativeAOT-clean. A Roslyn source generator emits an IRonFormatter<T> per [RonObject]; the resolver is a generic-static cache (Cache<T>.Formatter), so resolution is a single static-field read with zero boxing. [ModuleInitializer] self-registration — no scan.
  • Zero-allocation parse. Forward-only Utf8RonReader over ReadOnlySpan<byte>, SearchValues scanning, span value-decoders. Keys interned per-document.
  • Binds fields and properties, by a snake_case naming policy (override with [RonMember("…")], exclude with [RonIgnore]). Defaults come from the parameterless ctor → serde #[serde(default)] parity: a document naming a few keys leaves everything else at its ctor default.
  • struct and class both work as value targets — value types bind natively (no boxing), and List<struct> / Dictionary<string, V> are trivial.
  • Custom converters for any type via RonFormatterResolver.RegisterStatic<T> — newtypes, packed values, untagged unions (branch on the reader token), all plug in uniformly.
  • Enums are bare identifiers, accepted case-insensitively as either PascalCase (Dark) or snake_case (dark), serialized snake_case.
  • Indented, readable writer — one field per line; the format's whole appeal is legible deep nesting.
  • 133-test corpus covering scalars, nesting, collections, enums, comments, trailing commas, serde-default, unknown-field skip, malformed-input rejection, and serialize→deserialize fixed-point.

RON subset

Supported: structs Name( field: val ) (optional name), lists [ ], string-keyed maps { }, scalars (int with _ separators + radix, float incl. inf/-inf/NaN, bool, string with escapes and \u{XXXX}), bare-identifier enums, // and /* */ comments, trailing commas. Not yet implemented: explicit Some(x)/None Option tokens (nullable binds via missing-key→null), tuples without field names, char/raw-string literals.

Build

ronmamon is part of the holo-q tree and has a sibling path-dependency on Poet (the source-emission substrate the generator is authored on, analyzer-time only). Clone it alongside:

some-dir/
  ronmamon/        (this repo)
  Poet.cs/         (../../repo-kit/Poet.cs in the org tree)

Then dotnet build (or the org's rk build ronmamon). The generator project bundles Poet.dll into the analyzer load context, so a consumer needs only the two ProjectReferences (runtime + generator-as-analyzer).

Relation to Tomatonl

ronmamon is the nesting-native sibling of Tomatonl, the org's TOML lib — same architecture (Poet-generated binders, generic-static resolver, Utf8 ref-struct reader), minus TOML's section-header machinery, which RON's delimited nesting makes unnecessary.

License

MIT.

About

Zero-allocation UTF-8 RON (Rusty Object Notation) parser + source-generated binder for .NET / NativeAOT — the nesting-native sibling of Tomatonl

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages