Skip to content

Mid-D-Man/DixScript-Rust

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2,279 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

DixScript: The Swiss Army Knife of Data Formats

Config, Code, and Crypto in One .mdix File

License: MIT Rust Status

"I built this because I was tired of copy-pasting the same JSON config blocks 500 times. Turns out other people hate that too."
β€” Mid-D-Man, Creator


⚠️ Beta β€” Not Yet Production Battle-Tested

The Rust port is feature-complete but has not yet seen wide production use. The API is stable and the compiler pipeline is fully implemented. Use it in your own projects, test it, and give feedback β€” but treat it as beta for anything mission-critical until v1.0.0 is officially published.

Package Status
dixscript (core Rust library) βœ… Feature-complete, API stable
mdix-cli βœ… All commands implemented
mdix-ffi / C# bindings ⏳ Bindings generated, packaging pending
mdix-go ⏳ Header generated, Go wrapper pending
mdix-java / mdix-python ⏳ Pending runtime wrappers
mdix-wasm ⏳ Pending wasm-bindgen annotations
mdix-c ⏳ Header stable, examples pending

The C# prototype (https://github.com/Mid-D-Man/DixScript) remains the reference implementation for the language itself.


The Origin Story (Or: How Scope Creep Turned Into a Format)

Started as: A quick hack to make a mobile game's remote config less painful.

The Problem: The game had weapons, camos, attainment missions, and shop data spread across Unity Remote Config as massive nested JSON blobs. Adding a single new field to a camo definition meant updating it in dozens of places. One typo and suddenly every weapon in the game was broken at runtime β€” with no error until the player hit that screen.

Here's a real slice of what that looked like:

{
  "EquipableItemCamoClassId": "ALL_SMG_CAMOS_CONFIG",
  "InventoryItemCamos": [
    {
      "MainItemId": "ALIYAHOO419",
      "MainItemClass": "BASIC_SMG",
      "CamoRaritySubClass": [
        {
          "RaritySubClassId": "Aliyahoo419_Basic_Camos",
          "MainItemCamos": [
            {
              "CamoId": "ALIYAHOO419",
              "CamoIndex": 0,
              "CamoAvailableInSeason": "1",
              "CamoRarity": "Basic",
              "CamoAtlasSpriteName": "Aliyahoo419(Clone)",
              "CamoInGameName": "Aliyahoo419",
              "CamoType": "Sprite",
              "MaterialAddress": "Null"
            }
          ]
        }
      ]
    }
  ]
}

This is just the camo config for one weapon class. Three separate blobs, hundreds of lines, all duplicating the same structure.

The Solution: "What if the shape of a camo was defined once, and I just filled in the data?"

The Result: The same config in DixScript:

@ENUMS(
  WeaponClass { BASIC_SMG, RUNIC_SMG, LEGENDARY_SMG }
  CamoRarity  { Basic, Rare, Epic, Legendary, Runic }
  CamoType    { Sprite, SpriteAndMaterial }
)

@QUICKFUNCS(
  ~camo<object>(id, index<int>, rarity<enum>, sprite, inGameName, type<enum>) {
    return {
      CamoId                = id
      CamoIndex             = index
      CamoAvailableInSeason = "1"
      CamoRarity            = rarity
      CamoAtlasSpriteName   = $"{sprite}(Clone)"
      CamoInGameName        = inGameName
      CamoType              = type
      MaterialAddress       = "Null"
    }
  }

  ~rarityClass<object>(subClassId, camos) {
    return { RaritySubClassId = subClassId, MainItemCamos = camos }
  }

  ~weapon<object>(itemId, class<enum>, rarityClasses) {
    return { MainItemId = itemId, MainItemClass = class, CamoRaritySubClass = rarityClasses }
  }
)

@DATA(
  EquipableItemCamoClassId = "ALL_SMG_CAMOS_CONFIG"

  InventoryItemCamos::
    weapon("ALIYAHOO419", WeaponClass.BASIC_SMG, [
      rarityClass("Aliyahoo419_Basic_Camos", [
        camo("ALIYAHOO419", 0, CamoRarity.Basic, "Aliyahoo419", "Aliyahoo419", CamoType.Sprite)
      ]),
      rarityClass("Aliyahoo419_Epic_Camos", [
        camo("ALIYAHOO419_HORIZON", 0, CamoRarity.Rare, "Aliyahoo419_Horizon", "Aliyahoo419-Horizon", CamoType.Sprite),
        camo("ALIYAHOO419_ROSE",    1, CamoRarity.Epic, "Aliyahoo419_Rose",    "Aliyahoo419-Rose",    CamoType.Sprite)
      ])
    ])
)
Config JSON (formatted) DixScript Reduction
ALL_SMG_CAMOS_CONFIG ~350 lines ~110 lines 69%
ALL_SMG_EQ_AND_CAMO_ATTAINMENT_CONFIGS ~280 lines ~90 lines 68%
ALL_SMG_UNIQUE_CAMOS_MISSIONS_CONFIG ~230 lines ~75 lines 67%
All 3 configs combined ~860 lines ~275 lines ~68%

What Is DixScript?

DixScript is a data interchange format that combines:

  • πŸ“¦ Configuration (like TOML)
  • πŸ”§ Compile-time functions (like Jsonnet, but less cryptic)
  • πŸ”’ Built-in encryption (AES-256-GCM, not an afterthought)
  • πŸ—œοΈ Automatic compression (gzip/bzip2/lzma)
  • πŸ“‹ Type safety (enums, strong typing when you want it)
  • 🎯 Zero runtime dependencies (pure Rust, or C# in the original)

All in one file with a .mdix extension.


Why DixScript Exists (The Problem It Solves)

The Config File Problem

Modern projects have config sprawl: /config β”œβ”€β”€ base.json # 500 lines β”œβ”€β”€ development.json # 300 lines (80% duplicated from base) β”œβ”€β”€ production.json # 400 lines (90% duplicated) β”œβ”€β”€ secrets.env # Separate encryption β”œβ”€β”€ validation.js # Separate validation logic β”œβ”€β”€ build.sh # Compresses everything └── deploy.yaml # References all of the above You change one setting β†’ Update 3 files β†’ Run 2 scripts β†’ Hope you didn't break something.

The DixScript Solution

@DLM(DCompressor.gzip, DEncryptor.aes256)

@QUICKFUNCS(
  ~createEnemy<object>(name, health, damage) {
    return {
      name   = name,
      health = health,
      damage = damage,
      armor  = health / 10,
      xp     = health / 2,
      gold   = health / 4
    }
  }
)

@DATA(
  environment<enum> = Environment.PROD

  enemies::
    createEnemy("Goblin", 50, 10),
    createEnemy("Orc", 100, 20),
    createEnemy("Troll", 200, 40)
)

@SECURITY(
  encryption -> { mode = "keyfile", algorithm = "aes256-gcm" }
)

One file. Config, logic, encryption, compression. Done.


Key Features

1. Compile-Time Functions (QuickFuncs)

Write logic once, execute at compile time. No runtime overhead.

@QUICKFUNCS(
  ~calculateDamage<int>(base, difficulty<enum>) {
    multiplier = difficulty == Difficulty.HARD ? 2.0 : 1.0
    return Math.round(base * multiplier)
  }
)

@DATA(
  easy_enemy = { damage = calculateDamage(50, Difficulty.EASY) },
  hard_enemy = { damage = calculateDamage(50, Difficulty.HARD) }
)

2. Two-Tier Data System

@DATA(
  // Flat properties (single equals)
  app_name = "MyApp"
  version  = "1.0.0"
  port     = 8080

  // Table properties (single colon)
  server: host = "localhost", port = 8080, ssl = true

  // Group arrays (double colon)
  admins:: "alice", "bob", "charlie"
)

3. Optional Commas

Commas are optional between entries. Use horizontal style with commas or vertical style without β€” your choice.

4. Built-in Encryption & Compression

@DLM(DCompressor.gzip, DEncryptor.aes256)

@SECURITY(
  encryption -> { mode = "password" }
)

@DATA(
  api_key = "super_secret_key"
)

Compile: mdix compile secrets.mdix --password
Output: secrets.mdix.enc (compressed + encrypted)

5. Enums

@ENUMS(
  LogLevel    { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3 }
  Environment { DEV = 1, STAGING = 2, PROD = 3 }
)

@DATA(
  log_level<enum>    = LogLevel.INFO
  current_env<enum>  = Environment.PROD
)

6. Strong Types When You Need Them

@DATA(
  // Inferred
  count   = 42
  price   = 19.99
  enabled = true

  // Explicit
  max_users<int>   = 1000
  tax_rate<float>  = 0.15f
  color<hex>       = #FF5733
  avatar           = b:("base64data...")
  email_regex      = r:("^[a-z@.]+$")
  release_date     = 2025-12-31
)

Quick Comparison

Feature JSON YAML TOML Jsonnet DixScript
Deduplication via functions ❌ ❌ ❌ βœ… βœ…
Built-in encryption ❌ ❌ ❌ ❌ βœ…
Built-in compression ❌ ❌ ❌ ❌ βœ…
Enums ❌ ❌ ❌ ⚠️ βœ…
Optional commas ❌ βœ… ❌ ❌ βœ…
Compile-time execution ❌ ❌ ❌ βœ… βœ…
Zero runtime dependencies βœ… ❌ βœ… ❌ βœ…
Human-readable βœ… βœ… βœ… ⚠️ βœ…

Section Reference

@CONFIG(         // Compiler settings, metadata
  version -> "1.0.0"
)

@IMPORTS(        // Import from other .mdix files
  Utils from "common/utils.mdix"
)

@DLM(            // Data Lifecycle Modules β€” compression + encryption
  DCompressor.gzip
  DEncryptor.aes256
)

@ENUMS(          // Named constants
  Status { ACTIVE, INACTIVE, PENDING }
)

@QUICKFUNCS(     // Compile-time functions
  ~calculate<int>(x, y) {
    return x + y
  }
)

@DATA(           // Your actual data
  result = calculate(10, 20)
)

@SECURITY(       // Security configuration
  encryption -> { mode = "password" }
)

All sections are optional. Use what you need.


Current Status: Rust Port

Original: Written in C# (.NET 8), fully functional, available at https://github.com/Mid-D-Man/DixScript.
This Repo: Rust port β€” feature-complete, API stable, actively tested via CI.

Rust Port Progress

Component Status Notes
Utilities βœ… Complete Logger, keywords, helpers
ErrorManager βœ… Complete All 10 error types
Lexer βœ… Complete All token types
Parser βœ… Complete All 6 sections
Semantic Analyzer βœ… Complete All 8 analysis phases
QuickFuncs Resolver βœ… Complete Full function interpreter
Binary Serialization βœ… Complete Packer + Unpacker
DLM Pipeline βœ… Complete Forward and reverse
Runtime API βœ… Complete Load, access, build, convert
LSP Server βœ… Complete Full IDE integration
CLI βœ… Complete All commands
FFI / C βœ… Complete C header + 40+ exported functions

Language Wrapper Status

All wrappers bind to the Rust runtime via FFI. The core is complete β€” wrappers are pending packaging and publishing.

Package Language Status
mdix-ffi + C# NuGet C# / Unity ⏳ Bindings generated, NuGet packaging pending
mdix-go Go ⏳ C header generated, Go wrapper pending
mdix-java Java / Kotlin ⏳ Pending JNI wrappers
mdix-python Python ⏳ Pending PyO3 wrappers
mdix-wasm JS / Browser ⏳ Pending wasm-bindgen annotations
mdix-c C / C++ ⏳ Header stable, examples pending

Why Rust?

  • πŸš€ Performance: The C# prototype is fast. Rust is faster.
  • πŸ”§ Portability: Native binaries, WASM, embedded.
  • πŸ¦€ Safety: Ownership model catches bugs at compile time.
  • 🌐 FFI: One Rust core, wrappers for every major language.

Getting Started

# Build from source (crates.io publish coming soon)
git clone https://github.com/Mid-D-Man/DixScript-Rust
cd DixScript-Rust
cargo build -p mdix-cli --release

# Basic usage
mdix validate config.mdix
mdix compile config.mdix
mdix compile secrets.mdix --password
mdix convert config.json --to mdix
mdix inspect config.mdix --keys
use dixscript::Runtime::DixLoader;
use dixscript::Runtime::DixLoadOptions;

fn main() {
    let loader = DixLoader::new();
    let data = loader.load_text("config.mdix", &DixLoadOptions::new()).unwrap();
    let port: i32 = data.get("server.port").unwrap_or(8080);
    println!("Server on port {}", port);
}

When Should You Use DixScript?

Great for: Game data configs (weapons, items, enemies, levels), multi-environment server configs, encrypted secrets bundles, any schema where you're copy-pasting structure repeatedly.

Maybe not for: Tiny configs under 50 lines with no repetition, simple key-value stores (TOML is simpler), situations requiring maximum existing tooling support.

The rule of thumb: If changing one field currently means editing it in more than three places, DixScript will help.


Contributing

Contributions welcome. The Rust port is feature-complete β€” areas where help is most useful: language wrapper packaging (C#, Go, Python), LSP editor extensions, documentation, and test coverage.

Code style: Follow rustfmt.toml in the repo.


Documentation

  • Grammar spec: others/midx.ebnf
  • C# reference implementation: https://github.com/Mid-D-Man/DixScript
  • CI results: https://mid-d-man.github.io/DixScript-Rust/

License

MIT β€” use it however you want, commercial or personal. See LICENSE.


Contact

Creator: Mid-D-Man
GitHub: https://github.com/Mid-D-Man/DixScript-Rust
Original (C#): https://github.com/Mid-D-Man/DixScript

Questions? Found a bug? Open an issue.


"Config files shouldn't require a PhD to maintain." β€” The DixScript Philosophy

About

The core dixscript compiler ported from c# to rust fo the sake of improved performance and ffi,code bindingsm and what not

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors