Skip to content

TheBadkraft/anvil.net

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

218 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Anvil.Net

License .NET Version Parser API Build

.NET port of Anvil Engine β€” the zero-copy, typeless, blazing-fast data language relegating JSON, YAML, and TOML to the dustbin of embarrassing legacy mistakes. Never settle for good enough.

"JSON is what happens when a programmer hates humans. YAML is what happens when a human tries to write JSON while drunk. TOML is what happens when a config file tries to cosplay as INI. Anvil is what happens when a software engineer decides good enough sucks."

~ Badkraft, 2025

Anvil.Net is a zero-copy, typeless, allocation-minimal parser for the Anvil language family β€” written in pure C# 13 / .NET 9.
No dependencies. No apologies.


Language Overview (AML – Anvil Modelling Language)

#!aml
@[mod="anvil_test", version="1.0.0", debug=true]

// Single inheritance
base_block @[tier=1] := { hardness := 2.0 }
derived_block : base_block @[tier=2] := { hardness := 5.0 }

server @[core] := {
    name := "Anvil Survival"
    port := 25565
    motd := "Forged in fire."
}

world @[seed=1337] := {
    spawn @[respawn] := (0, 64, 0)
    rules @[hardcore] := [
        "pvp", "keepInventory=false", "naturalRegeneration=true"
    ]
}

readme := @md`# Anvil Server β€” Forged in fire. Built to last.`

player := ("Notch", 100, true)

Core features:

  • Rich literals: strings, numbers, booleans, null, hex (#FF5733, 0xDEADBEEF), bare identifiers
  • Objects { }, Arrays [ ], Tuples ( ), Blobs @tag`...`
  • Attributes @[key, key=value, …] β€” attach to any statement, object, array, or tuple
  • Single inheritance: derived : base := { … }
  • Three dialects: #!aml (modelling), #!asl (scripting), #!amp (messaging, scalars/blobs only)
  • Shebang detection, multi-line block comments, line comments

Current State (v1.6.0)

All major feature phases are shipped. v1.6.0 delivers the ANVL Schema Phase 2 enforcement layer: @[schema] context, the full schema.* attribute vocabulary, 47xx error codes, a 4-step schema path resolver, and 16 schema test cases including security boundary tests.

465 tests. Zero warnings.

  • ANVL Schema Phase 2 β€” .asch schema files use @[schema] module attribute; RHS bare-word tokens become type names (string, int, float, bool, date, any, arrays [ T ], tuples ( T, … ), nested blocks). AnvilSchemaValidator traverses data documents and enforces declared types. Anvil.LoadWithSchema() provides explicit schema binding for CI and runtime registry use cases. Type confusion, array element injection, and structurally invalid path values are rejected at the schema boundary (TC-16 SQL injection analogue, error codes 4701–4703).
  • Schema field attributes β€” @[schema.optional], @[schema.default="v"], @[schema.path] (structural path validation, no TOCTOU). Advisory vs Strict enforcement modes; same .asch file works for both.
  • Schema path resolver β€” 4-step resolution: absolute β†’ relative-to-file β†’ schema.path search entries β†’ implicit polaris/schemas/ fallback.
  • Schema validation β€” .asch schema documents declare Object, Enum, and Flags types. AnvilSchema loads, resolves, and validates data documents; all field violations in a document are collected before returning (SchemaRuleset, SchemaType, FieldRule, TryGetOwn(), 460x error codes).
  • POCO code generation β€” AnvilCodeGen converts a loaded schema directly into a C# source file (sealed classes, records, enums, [Flags] enums). Pure StringBuilder, no Roslyn dependency. All identifiers PascalCased. Controlled via CodeGenOptions.
  • AMP dialect β€” scalar arrays [v,…] and scalar tuples (v,…) fully parsed and round-tripped by AnvilWriter.
  • Multi-error validation β€” AnvilValidationResult collects every error across a document or a directory; ThrowIfInvalid() integrates with exception pipelines.
  • ASL scripting β€” C-style function bodies, for/break/continue, List/Map values, $key.field deep access, built-in modules (sys_math/sys_str/sys_io), recursion guard at 64 levels, host module registration via AnvilModuleRegistry.
    • Seam hardening β€” bare identifiers resolve from local scope only; the $ sigil is required to cross into the ANVL document layer.
    • Map literal syntax β€” { key: value, … } produces a runtime AslValueKind.Map; field access via m.key and chained m.inner.key.
    • String escape sequences β€” \n \t \r \" \\ processed at parse time in all string literals.
    • Interpolated strings β€” $"Hello, {name}!\n" β€” identifier holes resolved through the normal scope chain; result type is always String.
  • Anvil.LoadDirectory(path) β€” load and proxy every .aml/.asl/.anvl file in a directory as a single synthetic root; statements accessible as root["stem.Key"].
  • Multi-file import graphs β€” split large AML datasets across files with configurable aliases, cross-source lookup, VarRef resolution, and inheritance.

Anvil.Net.dll β€” the parser, a direct port. Never diverges from the Java/C cousins.

File Purpose
src/Source.cs Scanner cursor β€” shebang, dialect, whitespace/comment skipping
src/Context.cs Parse context β€” statement/attribute/field pools, Builder
src/Parser.cs Full AML/ASL/AMP parser β€” all value types
src/Types.cs Enums and metadata structs (AnvilValueMeta, AnvilStatement, …)
src/Symbols.cs Symbol table
src/Operators.cs Operator table
src/Errors.cs Error codes and thread-local error state
src/Constants.cs Dialect enum, versioning, encoding helpers
src/AslValue.cs Runtime value type (AslValueKind: Null/Int/Float/String/Bool/List/Map)
src/AslScope.cs Scope chain for ASL function execution
src/AslNode.cs AST node types for the ASL evaluator
src/AslOp.cs ASL opcode table (includes For/Break/Continue/ListLiteral/MapLiteral)
src/AslParser.cs ASL function-body parser
src/AslEvaluator.cs Tree-walking evaluator
src/AslFunctionMeta.cs Parsed function metadata + Execute() entry
src/AslExecutionContext.cs Per-call execution context
src/IAnvilModule.cs Module identity interface (Name property)
src/IAnvilCallableModule.cs Module callable interface (Invoke(method, args))
src/AnvilModuleRegistry.cs Global thread-safe module registry (two-tier: built-ins + user)
src/modules/SysMathModule.cs Built-in sys_math module (abs/min/max/pow/sqrt/…)
src/modules/SysStrModule.cs Built-in sys_str module (upper/lower/split/join/…)
src/modules/SysIoModule.cs Built-in sys_io module (exists/readFile/writeFile/lines/…)

Anvil.Net.Api.dll β€” the fluent .NET API layer. References Anvil.Net and gets access to parser internals via InternalsVisibleTo. Ships separately so the parser never has to change to satisfy API concerns.

File Purpose
src/Anvil.cs Static entry point β€” Anvil.Load(), Anvil.Parse(), Anvil.LoadDirectory(), LoadResolved()
src/AnvilNode.cs Single consumer type β€” indexers, TryGet, TryGetOwn, AsSpan/AsMemory, HasBase, Is(), Resolve()
src/AnvilNodeExtensions.cs AsInt(), AsFloat(), AsValues<T…>(), GetX(key, default)
src/AnvilNodeState.cs Per-root lazy merge cache for inheritance resolution
src/AnvilMemberAttribute.cs [AnvilMember("key")] β€” maps POCO properties to AML keys
src/AnvilSerializer.cs Reflection-based POCO deserializer β€” Load<T>(), Parse<T>(), Deserialize<T>()
src/AnvilScriptEngine.cs Public script host β€” Execute(name, args…), HasFunction, FunctionNames
src/AnvilSchema.cs Schema entry β€” Load, Parse, Types, Validate, ValidateAll
src/schema/ SchemaRuleset, SchemaType, FieldRule, SchemaTypeKind, SchemaResolver, SchemaValidator
src/AnvilCodeGen.cs Code-gen entry β€” GenerateSource(AnvilSchema), GenerateSource(path)
src/codegen/ CSharpEmitter (internal renderer), CodeGenOptions
src/resolver/AnvilResolver.cs Kahn's sort + cycle detection; builds AnvilNodeState

API Usage (Anvil.Net.Api)

Deliberately minimal β€” one public type, full LINQ, zero friction.

// Load from file or parse a raw string
AnvilNode? root = Anvil.Load("server.aml");
AnvilNode? root = Anvil.Parse(sourceString);

// Always check for parse errors
if (root is null)
{
    var err = AnvilError.Current;
    Console.Error.WriteLine($"{err.Line}:{err.Column} β€” {err.Message}");
    return;
}

// Navigate with indexers β€” throws AnvilKeyNotFoundException on miss
string name = root["server"]["name"].AsString();
int    port = root["server"]["port"].AsInt();

// Arrays and tuples are IEnumerable<AnvilNode> β€” LINQ works natively
var rules = root["world"]["rules"]
              .Select(r => r.AsString())
              .ToList();

// Tuple elements by index
int x = root["world"]["spawn"][0].AsInt();
int y = root["world"]["spawn"][1].AsInt();
int z = root["world"]["spawn"][2].AsInt();

// Tuple deconstruction via extension method
var (rx, ry, rz) = root["world"]["spawn"].AsValues<int, int, int>();

// Attributes β€” HasAttribute / Attribute(key) returns AnvilNode?
bool experimental = root.HasAttribute("experimental");
long seed         = root["world"].Attribute("seed")!.AsLong();

// Enumerate all attributes
foreach (var attr in root.Attributes)
    Console.WriteLine($"@{attr.Identifier} = {attr.AsString()}");

// Blobs β€” AsString() returns the raw backtick content
string content = root["readme"].AsString();
string? tag    = root["readme"].BlobTag;   // "md", "sql", etc.

// Optional key β€” Attribute() returns null on miss; indexer throws
AnvilNode? opt = root["server"].Attribute("optional");
string val = opt?.AsString() ?? "default";

Schema Validation

// Load a schema document (.asch or @[schema] AML)
var schema = AnvilSchema.Load("mods.asch");
if (schema is null) { Console.Error.WriteLine(Anvil.LastError); return; }

// Validate a data document
var result = schema.Validate("data/stone.aml");
result.ThrowIfInvalid();

// Validate many files at once
var all = schema.ValidateAll(Directory.GetFiles("data", "*.aml"));
foreach (var err in all.Errors)
    Console.Error.WriteLine($"{err.FilePath}:{err.Line}  {err.Message}");

POCO Code Generation

// Generate C# from a schema β€” default options (nullable, sealed class, no namespace)
var source = AnvilCodeGen.GenerateSource(schema);
File.WriteAllText("Config.g.cs", source);

// With options
var source = AnvilCodeGen.GenerateSource(schema, new CodeGenOptions {
    Namespace      = "MyMod.Config",
    UseRecords     = true,
    NullableEnabled = true,
});

// One-shot from file path
var source = AnvilCodeGen.GenerateSource("mods.asch");

Direct Parser Access (Anvil.Net only)

For tooling or consumers that don't want the API layer:

var ctx = new AnvilContext.Builder()
    .LoadFile("server.aml")
    .Build();

if (ctx?.Parse() != true)
{
    Console.Error.WriteLine(AnvilError.Current);
    return;
}

string src = File.ReadAllText("server.aml");
foreach (var stmt in ctx.Statements)
    Console.WriteLine($"[{stmt.ValueMeta?.Type}] {stmt.GetIdentifier(src)}");

ASL Script Execution

Execute ASL functions from C# using AnvilScriptEngine:

// 1. Parse ASL script
var script = """
    #!asl
    add := (a, b) => { return a + b; }
    greet := (name) => { return "Hello " + name + "!"; }
    """;

var root = Anvil.Parse(script, AnvilDialect.Asl)!;

// 2. Get script engine
var engine = root.GetScriptEngine()!;

// 3. Execute functions
var sum = engine.Execute("add", 
    AslValue.FromInt(5), 
    AslValue.FromInt(3));
Console.WriteLine(sum!.Value.LongValue);  // 8

var greeting = engine.Execute("greet", 
    AslValue.FromString("Alice"));
Console.WriteLine(greeting!.Value.StringValue);  // "Hello Alice!"

Register callable modules for ASL scripts:

// 1. Implement IAnvilCallableModule
public sealed class MathModule : IAnvilCallableModule {
    public string Name => "Math";
    
    public AslValue Invoke(string method, AslValue[] args) {
        return method switch {
            "sqrt" => AslValue.FromFloat(Math.Sqrt(args[0].ToDouble())),
            "pow"  => AslValue.FromFloat(Math.Pow(args[0].ToDouble(), args[1].ToDouble())),
            _ => AslValue.Null
        };
    }
}

// 2. Register BEFORE loading scripts
AnvilModuleRegistry.Register(new MathModule());

// 3. ASL scripts can now call Math.sqrt(x)
var script = """
    #!asl
    using Math
    
    calculate := (x) => {
        sqrt_x := Math.sqrt(x);
        return Math.pow(sqrt_x, 2);
    }
    """;

var result = engine.Execute("calculate", AslValue.FromFloat(16.0));
// result: 16.0

Event hook integration:

public class GameEventBus {
    private readonly AnvilScriptEngine _engine;
    
    public GameEventBus(VoxelWorld world) {
        // Register World module
        AnvilModuleRegistry.Register(new WorldModule(world));
        
        // Load hook scripts
        var hooks = Anvil.Load("hooks.asl", AnvilDialect.Asl)!;
        _engine = hooks.GetScriptEngine()!;
    }
    
    public void OnPlayerJoined(string username, uint netId) {
        _engine.Execute("on_player_joined",
            AslValue.FromString(username),
            AslValue.FromInt(netId));
    }
}

πŸ“– Complete Documentation: See docs/ASL-Execution-API.md for:

  • Full API reference
  • Module system details
  • Thread safety considerations
  • Current limitations & workarounds
  • Planned features (per-invocation module context)

Roadmap

Version Focus Status
0.1.0 Zero-copy parser core β€” full AML/ASL/AMP (Anvil.Net.dll) βœ… Released (alpha)
0.1.1 Fluent runtime API β€” AnvilNode, Anvil.Load(), LINQ (Anvil.Net.Api.dll) βœ… Released (alpha)
0.1.3 Safe navigation, AsSpan/AsMemory, transparent lazy inheritance resolver, POCO deserialization βœ… Released (alpha)
0.2.0 vars block, .ref VarRefs, $"\u2026" interpolation; ReadOnlyMemory<char> backing βœ… Released (alpha)
0.2.1 AnvilWriter β€” round-trip, pretty-print, minification, dialect support βœ… Released (alpha)
0.2.2 [AnvilArray/Map/Tuple/Quoted], POCO write side, blob parser fix, string quoting βœ… Released (alpha)
0.2.3 O(1) statement hash index (540Γ— TryGet miss), NodeEnumerator struct, benchmarks suite βœ… Released (alpha)
0.3.0 Multi-file import graphs, cross-source lookup/VarRef/inheritance, MergeBenchmarks βœ… Released (alpha)
0.4.0 ASL scripting layer β€” functions, modules, using, AnvilScriptEngine, Anvil.LoadDirectory() βœ… Released (alpha)
0.4.1 ASL completeness β€” C-style body syntax (=, ;, ++/--, +=/-=/*=//=), for/else if/break/continue, flag-based control flow (no exception on return), List/Map values, $key.field deep access, built-in modules (sys_math/sys_str/sys_io), error context FunctionName βœ… Released (alpha)
0.4.2 @[attr] on ASL function declarations, AnvilScriptEngine.FunctionNode() βœ… Released (alpha)
0.4.3 AMP scalar arrays [v,…] and scalar tuples (v,…), AmpArrayElementNotScalar=4401, AnvilWriter AMP array/tuple support βœ… Released (alpha)
0.4.4 Multi-error validation β€” AnvilValidationError, AnvilValidationResult, Anvil.Validate*(), AnvilValidationException, ValidateAll() aggregate result βœ… Released (alpha)
0.4.5 Schema validation β€” AnvilSchema, SchemaRuleset, .asch files, @[schema] marker, TryGetOwn(), SC01–SC20 βœ… Released (alpha)
0.4.6 POCO code-gen β€” AnvilCodeGen, CSharpEmitter (pure StringBuilder), CodeGenOptions βœ… Released (alpha)
1.0.0-rc1 Schema + codegen integration, full test suite, documentation pass βœ… Released
1.0.0-rc2 No-throw parsing policy enforced, 11 new ASL parse error codes (450x range) βœ… Released
1.0.0-rc3 SchemaValidator zero-alloc happy path, AslScope inline 8-slot linear table, SE09 fix βœ… Released
1.0.0-rc4 Interpolated string slot syntax fix ({ref} bare name), dotnet pack NuGet toolchain βœ… Released
1.1.0 Anonymous block syntax (FR-001), custom merge strategy API (FR-002) β€” IMergeStrategy, MergeStrategies.ConcatArrays/FilterBase/Compose, AnvilResolveOptions, public AnvilField/AnvilValue βœ… Released
1.2.0 Lazy array concatenation (FR-003) β€” IAnvilArrayView, ConcatenatedArrayView, VirtualArrayView, full ConcatArrays() implementation with zero-copy virtual views βœ… Released
1.3.0 Immutable function declarations, first-class function values (AslValueKind.Function), closure semantics, error 5504 on reassign βœ… Released
1.4.0 Lambda expressions, AnvilScriptEngine recursion guard, AslCallDepthExceeded, FunctionName on error context, SE09-SE12 test coverage βœ… Released
1.5.0 while loop, AstTests coverage, ImmutableFunctionTests, VarsTests, pass-by-value semantics hardening βœ… Released
1.5.1 @[attr] on ASL function declarations refinements, AnvilScriptEngine.FunctionNode(), test suite consolidation βœ… Released
1.5.2 Seam hardening β€” bare-ident ExternalLookup fallthrough removed (BL-040-asl); $ sigil now required to cross ANVL/ASL boundary βœ… Released
1.6.0 ANVL Schema Phase 2 β€” @[schema] context switch, schema.* vocabulary, validator, AnvilSchemaValidator, Anvil.LoadWithSchema(), 4-step path resolver, 47xx error codes, security boundary enforcement (TC-16), 16 schema test cases, .asch authoring guide βœ… Released

Building

cd anvil.net
dotnet build

Requires .NET 9 SDK.


License

Proprietary Β© 2025–2026 Quantum Override. All rights reserved.

This software may not be used, copied, modified, or distributed without explicit written permission. See LICENSE for full terms.

Anvil Messaging Protocol (AMP) is a separately licensable dialect. Contact licensing@quantumoverride.dev to obtain a commercial AMP license.


"Anvil β€” because your data deserves better than 1998."

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages