DixScript - The only data format you'll ever need. Born from frustration with JSON's verbosity, TOML's limitations, and YAML's whitespace nightmares. I built DixScript for my Unity game projects because I was tired of copy-pasting the same config values everywhere. Then I realized: if I need this, probably you do too.
What makes DixScript special?
- ✅ 27-34% smaller than JSON/TOML - Built-in deduplication (the bigger your file, the better it works)
- ✅ Compile-time functions - Stop repeating yourself. Write
~calculateDamage<int>(base, multiplier)once, use everywhere - ✅ Built-in encryption - AES-256-GCM at the format level (not an afterthought)
- ✅ Zero dependencies - Pure C#, works everywhere (.NET 8+)
- ✅ Railway-oriented programming -
Result<T, Error>types, no exceptions (unless you want them) - ✅ Smart type inference - Explicit types when you need them, inferred when you don't
- ✅ One file, one truth - Your config, computed values, enums, and security all in one place
Originally built for: Unity game configurations, hot-reloading, encrypted save files
Actually useful for: Game dev, Web3 configs, API services, ML pipelines, e-commerce, enterprise config management, literally anywhere you use JSON/TOML/YAML
File extension: .mdix (MidMans Data Interchange Extension Script)
Simplest possible DixScript file:
@DATA(
app_name = "MyApp",
version = "1.0.0",
port = 8080
)
That's it. Load it in C#:
using DixScript.Runtime;
var result = Dix.Load("config.mdix");
result.Match(
data => {
string appName = data.Get<string>("app_name").UnwrapOr("Unknown");
int port = data.Get<int>("port").UnwrapOr(8080);
Console.WriteLine($"{appName} running on port {port}");
},
error => Console.WriteLine($"Error: {error}")
);dotnet add package DixScript --version 1.0.0git clone https://github.com/Mid-D-Man/DixScript.git
cd DixScript
dotnet build -c Release# Add to PATH (Windows)
setx PATH "%PATH%;C:\path\to\DixScript\bin\Release\net8.0"
# Add to PATH (Linux/macOS)
export PATH=$PATH:/path/to/DixScript/bin/Release/net8.0
# Verify installation
dixscript --version
# Output: DixScript Compiler v1.0.0The bigger your config gets, the more DixScript shines.
JSON (350 lines, lots of duplication):
{
"enemies": [
{"name": "Goblin", "health": 50, "damage": 10, "xp": 25},
{"name": "Orc", "health": 100, "damage": 20, "xp": 50},
{"name": "Troll", "health": 200, "damage": 40, "xp": 100}
]
}DixScript (same data, but with a function):
@QUICKFUNCS(
~createEnemy<object>(name, health, damage) {
return {
name = name,
health = health,
damage = damage,
xp = health / 2 // Computed value!
}
}
)
@DATA(
enemies::
createEnemy("Goblin", 50, 10),
createEnemy("Orc", 100, 20),
createEnemy("Troll", 200, 40)
)
Result: 34% smaller file, zero duplication, one source of truth.
No exceptions thrown (unless you explicitly unwrap without checking). Everything returns Result<TSuccess, TError>:
// ✅ Safe pattern matching
var result = Dix.Load("config.mdix");
result.Match(
success => Console.WriteLine("Loaded!"),
error => Console.WriteLine($"Failed: {error}")
);
// ✅ Chaining operations
var port = Dix.Load("config.mdix")
.Map(data => data.Get<int>("port"))
.UnwrapOr(8080);
// ✅ Early return pattern
if (!result.IsSuccess)
{
return Result<Response, string>.Err(result.Error);
}
var data = result.SuccessResult;QuickFunctions execute during compilation, NOT at runtime.
@QUICKFUNCS(
~calculateTax<float>(price) {
return price * 0.15
}
)
@DATA(
base_price = 100.0,
tax = calculateTax(100.0) // Executed at compile time
)
After compilation, when you load the file:
var data = Dix.Load("shop.mdix").SuccessResult;
float tax = data.Get<float>("tax").SuccessResult; // Value is 15.0 (pre-computed!)The source file shows calculateTax(100.0) for readability, but the runtime gets the computed value (15.0).
DixScript files are organized into 5 optional sections. All sections are optional unless you use certain features (e.g., DEncryptor requires @SECURITY).
| Section | Purpose | Required? |
|---|---|---|
@CONFIG |
Compiler settings, metadata | Optional |
@DLM |
Data Lifecycle Modules (compression, encryption, auditing) | Optional |
@ENUMS |
Named constants | Optional |
@QUICKFUNCS |
Compile-time functions | Optional |
@DATA |
Your actual data (the payload) | Optional |
@SECURITY |
Security configuration (auto-generated if missing with DLM encryption) | Optional |
Recommended order:
@CONFIG → @DLM → @ENUMS → @QUICKFUNCS → @DATA → @SECURITY
(But you can put them in any order!)
@DATA(
app_name = "MyApp",
debug = true,
max_connections = 100
)
@ENUMS(
LogLevel { DEBUG, INFO, WARN, ERROR }
)
@DATA(
log_level<enum> = LogLevel.INFO,
enable_debug = true
)
@QUICKFUNCS(
~add<int>(a, b) {
return a + b
}
)
@DATA(
result = add(10, 20) // Compiles to: result = 30
)
@DATA(
app_name = "MyApp"
server: host = "localhost", port = 8080, ssl = true
database: host = "db.local", port = 5432, pool_size = 10
)
Equivalent to:
{
"app_name": "MyApp",
"server": {
"host": "localhost",
"port": 8080,
"ssl": true
},
"database": {
"host": "db.local",
"port": 5432,
"pool_size": 10
}
}@DATA(
allowed_ports:: 8080, 8443, 9000
admins:: "alice", "bob", "charlie"
users::
{ name = "Alice", role = "admin" },
{ name = "Bob", role = "user" }
)
| Type | Example | Default Value |
|---|---|---|
int |
42, -100 |
0 |
float |
3.14f, -0.5f |
0.0 |
double |
3.141592653589793 |
0.0 |
string |
"hello", 'world' |
"" |
bool |
true, false |
false |
hex |
0xFF00AA, #FF0000 |
0x0 |
DixScript supports explicit float literals using the f suffix:
@DATA(
// Explicit float literals
price<float> = 19.99f,
tax<float> = 0.15f,
// Without suffix = double (default)
pi = 3.141592653589793,
// Type annotation forces conversion
auto_float<float> = 1.0 // Converts double → float
)
| Type | Syntax | Description |
|---|---|---|
array |
[1, 2, 3] |
Homogeneous array (all same type) |
tuple |
t:(1, "text", true) |
Mixed types (max 4 elements) |
object |
{ x = 10, y = 20 } |
Nested structure |
enum |
LogLevel.INFO |
Enum value reference |
@DATA(
// Blob (binary data as base64)
avatar = b:("SGVsbG8gV29ybGQ="),
// Regex pattern
email_validator = r:("^[a-z]+@[a-z]+\.[a-z]+$"),
// Date (ISO 8601)
release_date = 2025-01-15,
// Timestamp (ISO 8601)
created_at = 2025-01-15T10:30:00Z,
// Hex color
primary_color = #FF5733,
background = #1A2B3CFF
)
@DATA(
// Type inferred from value
count = 42, // <int>
price = 19.99f, // <float>
name = "Product", // <string>
enabled = true, // <bool>
// Explicit type annotation
max_users<int> = 1000,
tax_rate<float> = 0.15f
)
Continue to Part 2: Core Sections Deep Dive to learn about:
- @CONFIG section (compiler settings)
- @DATA section (two-tier structure, table properties, group arrays)
- @ENUMS section (named constants)
- Working with nested data
- Array homogeneity rules
Controls compiler behavior and operational settings. Completely optional - DixScript works fine without it.
@CONFIG(
key1 -> value1,
key2 -> value2,
key3 -> value3
)
| Key | Type | Values | Default | Description |
|---|---|---|---|---|
version |
string | "1.0.0" | "1.0.0" | DixScript version |
encoding |
string | "UTF-8", "UTF-16" | "UTF-8" | File encoding |
author |
string | Any string | "Unknown" | Author name |
created |
timestamp | ISO 8601 | Current time | Creation timestamp |
features |
string | "basic", "advanced" | "basic" | Feature control |
debug_mode |
string | "off", "regular", "verbose" | "regular" | Debug output level |
error_handling |
string | "halt", "continue", "recover" | "halt" | Error strategy |
compatibility_mode |
string | "strict", "best_effort", "permissive" | "strict" | Version handling |
Basic Mode (default):
@CONFIG(features -> "basic")
// Enables: @CONFIG, @DATA (simple), @DLM (basic)
// Disables: @QUICKFUNCS, @ENUMS, complex DATA features
Advanced Mode (everything enabled):
@CONFIG(features -> "advanced")
// Enables: All sections, all features
Selective Features (pick what you need):
@CONFIG(features -> "data,quickfuncs,enums")
// Enables only specified sections
off - Production mode (fastest):
@CONFIG(debug_mode -> "off")
// No debug output, errors only
regular - Development mode (recommended):
@CONFIG(debug_mode -> "regular")
// Parse progress, section status, error counts
verbose - Full diagnostics (debugging):
@CONFIG(debug_mode -> "verbose")
// Token stream, AST nodes, registry lookups, performance metrics
halt - Stop on first error (recommended for production):
@CONFIG(error_handling -> "halt")
// Fail fast, strict validation
continue - Find all errors (recommended for development):
@CONFIG(error_handling -> "continue")
// Mark errors but continue parsing
// Report all issues at end
recover - Attempt fixes (permissive parsing):
@CONFIG(error_handling -> "recover")
// Try to recover from errors
// Insert sensible defaults
@CONFIG(
version -> "1.0.0",
author -> "GameStudio",
created -> 2025-01-15T10:30:00Z,
encoding -> "UTF-8",
features -> "advanced",
debug_mode -> "regular",
error_handling -> "halt",
compatibility_mode -> "strict"
)
The @DATA section stores your actual data. It uses a two-tier ordering system inspired by TOML.
TIER 1: Flat Properties (must come first)
- Simple key-value pairs
- Must be declared before any grouped data
- Use commas between entries
TIER 2: Grouped Data (comes after flat properties)
- Table properties (single colon
:) - Group arrays (double colon
::) - Can be freely intermixed
- NO commas between groups
Pattern 1: Flat Properties Only
@DATA(
name = "test",
value = 42,
enabled = true
)
✅ Commas between properties
Pattern 2: Grouped Data Only
@DATA(
server.config: host = "localhost", port = 3000
database.settings: timeout = 5000, pool = 10
users.admins:: "alice", "bob"
)
❌ NO commas between group blocks
✅ Commas within table properties
✅ Commas within group arrays
Pattern 3: Flat Then Grouped (most common)
@DATA(
app_name = "MyApp",
version = "1.0.0",
port = 8080
server.config: host = "localhost", ssl = true
database.primary: host = "db.local", port = 5432
admins:: "alice", "bob"
)
✅ Commas between flat properties
❌ NO trailing comma after last flat property
❌ NO commas between grouped blocks
Assign multiple properties to a nested path:
@DATA(
// Single-line
server.config: host = "localhost", port = 8080, ssl = true
// Multi-line (same thing)
database.connection:
host = "db.server.com",
port = 5432,
username = "admin",
max_connections = 100
)
Equivalent to:
{
"server": {
"config": {
"host": "localhost",
"port": 8080,
"ssl": true
}
},
"database": {
"connection": {
"host": "db.server.com",
"port": 5432,
"username": "admin",
"max_connections": 100
}
}
}Create arrays at nested paths:
@DATA(
// Simple values
tags.system:: "production", "critical", "monitored"
// Objects
users.admins::
{ name = "Alice", role = "super" },
{ name = "Bob", role = "admin" }
// Numbers
ports.allowed:: 8080, 8443, 9000
)
Equivalent to:
{
"tags": {
"system": ["production", "critical", "monitored"]
},
"users": {
"admins": [
{"name": "Alice", "role": "super"},
{"name": "Bob", "role": "admin"}
]
},
"ports": {
"allowed": [8080, 8443, 9000]
}
}Complex nested data structures:
@DATA(
config = {
server = {
host = "localhost",
port = 8080
},
database = {
enabled = true,
connections = 100
}
}
)
IMPORTANT: In @DATA section, objects ALWAYS use = for properties!
// ✅ CORRECT
config = {
host = "localhost",
port = 8080
}
// ❌ WRONG (colon syntax is for QuickFuncs expressions only)
config = {
host: "localhost",
port: 8080
}
Arrays must contain elements of the same type:
@DATA(
// ✅ VALID: All integers
numbers = [1, 2, 3, 4, 5],
// ✅ VALID: All strings
names = ["Alice", "Bob", "Charlie"],
// ✅ VALID: All objects (different keys OK!)
users = [
{ name = "Alice", age = 30 },
{ name = "Bob", role = "admin" } // Different keys are fine!
],
// ❌ INVALID: Mixed types
// mixed = [1, "text", true] // Compile error!
// ✅ USE TUPLE for mixed types
mixed_data = t:(1, "text", true, 3.14)
)
For object arrays: All elements must be objects, but they can have different properties (just like JSON).
All collection types have a maximum nesting depth of 5 levels:
@DATA(
// ✅ Depth 1
level1 = [1, 2, 3],
// ✅ Depth 2
level2 = [[1, 2], [3, 4]],
// ✅ Depth 3
level3 = [[[1, 2]]],
// ✅ Depth 4
level4 = [[[[1, 2]]]],
// ✅ Depth 5 (MAXIMUM)
level5 = [[[[[1, 2]]]]],
// ❌ Depth 6 - COMPILE ERROR
// level6 = [[[[[[1, 2]]]]]]
)
Depth calculation: Each collection type (array [], object {}, tuple t:()) adds 1 level.
Define named constants for better code organization. Available in advanced mode only.
@ENUMS(
EnumName1 { VALUE1, VALUE2, VALUE3 }
EnumName2 { ITEM_A = 10, ITEM_B = 20, ITEM_C }
)
Rules:
- ❌ NO commas between enum declarations
- ✅ Commas between enum fields
- ✅ Auto-incrementing values (if not specified)
- ✅ Custom integer values allowed
@ENUMS(
LogLevel { DEBUG, INFO, WARN, ERROR }
// DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3
)
@ENUMS(
HttpStatus {
OK = 200,
CREATED = 201,
BAD_REQUEST = 400,
NOT_FOUND = 404
}
)
@ENUMS(
UserRole {
GUEST = 0,
USER = 1,
MODERATOR = 5,
ADMIN, // 6 (auto-increment from 5)
SUPER_ADMIN // 7
}
)
@ENUMS(
LogLevel { DEBUG, INFO, WARN, ERROR }
UserRole { GUEST, USER, ADMIN }
)
@DATA(
current_role<enum> = UserRole.ADMIN,
log_level<enum> = LogLevel.INFO,
// Enums can be used anywhere values are expected
default_role<enum> = UserRole.GUEST
)
The <enum> type is special:
- Accepts ANY enum value from ANY defined enum
- Stores both enum name and value at runtime
- Type-safe: can only assign valid enum values
@ENUMS(
Color { RED = 1, GREEN = 2, BLUE = 3 }
Size { SMALL = 10, MEDIUM = 20, LARGE = 30 }
)
@DATA(
// Both are valid <enum> types
primary_color<enum> = Color.RED,
shirt_size<enum> = Size.LARGE
)
@DATA(
game.player.stats: health = 100, mana = 50, level = 5
game.player.inventory: gold = 1000, items = 25
game.world.settings: difficulty = 2, pvp_enabled = true
)
Access in C#:
var health = data.Get<int>("game.player.stats.health").SuccessResult;
var gold = data.Get<int>("game.player.inventory.gold").SuccessResult;@DATA(
server.config: host = "localhost", port = 8080
server.allowed_ips::
"192.168.1.100",
"192.168.1.101"
database.primary: host = "db.local", port = 5432
database.replicas::
{ host = "db-replica-1", port = 5432 },
{ host = "db-replica-2", port = 5432 }
)
@ENUMS(
Environment { DEV, STAGING, PROD }
LogLevel { DEBUG, INFO, WARN, ERROR }
)
@DATA(
app_name = "E-Commerce API",
version = "2.0.0",
environment<enum> = Environment.PROD
server.config:
host = "0.0.0.0",
port = 8080,
workers = 4,
timeout = 30000
logging.settings:
level<enum> = LogLevel.INFO,
enabled = true,
max_file_size = 10485760
database.primary:
host = "prod-db.company.com",
port = 5432,
username = "api_user",
max_pool_size = 50
database.replicas::
{ host = "replica-1.company.com", port = 5432, readonly = true },
{ host = "replica-2.company.com", port = 5432, readonly = true }
api.rate_limits::
{ endpoint = "/auth", requests_per_minute = 10 },
{ endpoint = "/products", requests_per_minute = 100 },
{ endpoint = "/orders", requests_per_minute = 50 }
cache.settings = {
enabled = true,
ttl_seconds = 3600,
max_size_mb = 512,
eviction_policy = "LRU"
}
)
Load and access in C#:
var result = Dix.Load("api-config.mdix");
result.Match(
data => {
// Simple values
string appName = data.Get<string>("app_name").SuccessResult;
// Nested values
int port = data.Get<int>("server.config.port").SuccessResult;
int workers = data.Get<int>("server.config.workers").SuccessResult;
// Enum values
var env = data.Get<string>("environment").SuccessResult;
// Array access
var replicas = data.Get<List<Dictionary<string, object>>>("database.replicas").SuccessResult;
// Object access
var cacheEnabled = data.Get<bool>("cache.settings.enabled").SuccessResult;
Console.WriteLine($"{appName} v{data.Get<string>("version").SuccessResult}");
Console.WriteLine($"Running on port {port} with {workers} workers");
},
error => Console.WriteLine($"Failed to load: {error}")
);@DATA(
debug = true,
max_connections = 100,
timeout = 5000,
api_key = "secret_key_here"
)
@DATA(
server: host = "localhost", port = 8080
database: host = "db.local", port = 5432
redis: host = "cache.local", port = 6379
)
@DATA(
servers::
{ name = "web-1", ip = "10.0.0.1", role = "frontend" },
{ name = "web-2", ip = "10.0.0.2", role = "frontend" },
{ name = "api-1", ip = "10.0.0.3", role = "backend" }
)
@DATA(
// Flat properties first
app_name = "MyApp",
version = "1.0.0",
debug = false
// Then grouped data
server: host = "localhost", port = 8080
database: host = "db.local", port = 5432
admins:: "alice", "bob"
allowed_ips:: "192.168.1.100", "192.168.1.101"
)
Continue to Part 3: QuickFunctions Deep Dive to learn about:
-
Minimal QuickFunction syntax
-
Scope declarations (optional!)
-
Parameter type annotations (optional!)
-
Calling QuickFunctions from DATA
-
Calling QuickFunctions from other QuickFunctions
-
Built-in function registry
-
Real-world deduplication examples
QuickFunctions are compile-time functions that eliminate duplication. They execute during compilation, not at runtime. The bigger your config file, the more powerful they become.
Key principle: Write logic once, use everywhere. DixScript computes values at compile-time and bakes them into the output.
The absolute minimum:
@QUICKFUNCS(
~calculate<int>(a, b) {
return a + b
}
)
Required:
~prefix (tilde) - marks it as a QuickFunction- Function name (
calculate) - Return type annotation (
<int>) - CANNOT be void or null - Parameters in parentheses (
a, b) - Body with
returnstatement
Optional:
- Parameter type annotations
- Scope declaration
- Default parameter values
@QUICKFUNCS(
~add<int>(x, y) {
return x + y
}
)
@DATA(
result = add(10, 20) // Compiles to: result = 30
)
That's it! No scope needed, no parameter types needed. DixScript infers everything.
You can add types to parameters for clarity, but it's completely optional:
@QUICKFUNCS(
// No type annotations (DixScript infers)
~multiply<int>(a, b) {
return a * b
}
// With type annotations (explicit)
~divide<float>(numerator<float>, denominator<float>) {
return numerator / denominator
}
// Mixed (some typed, some not)
~calculate<int>(base<int>, multiplier) {
return base * multiplier
}
)
When to use type annotations:
- You want explicit documentation
- You need type safety for complex operations
- You're working with enums or special types
When to skip them:
- Simple arithmetic
- String operations
- Boolean logic
Scope controls WHERE a function can be called.
Default (no scope): Function is globally accessible everywhere in @DATA
@QUICKFUNCS(
// No scope = global by default
~calculateTax<float>(price) {
return price * 0.15
}
)
@DATA(
item_price = 100.0,
tax = calculateTax(100.0) // Can call from anywhere!
)
Explicit global scope:
@QUICKFUNCS(
~calculateTax<float> => global(price) {
return price * 0.15
}
)
Scoped to specific path:
@QUICKFUNCS(
// Only callable within server.config path
~validatePort<bool> => server.config(port<int>) {
return port > 1024 && port < 65536
}
)
@DATA(
server.config:
port = 8080,
is_valid = validatePort(port) // ✅ OK - inside server.config
// global_valid = validatePort(8080) // ❌ ERROR - wrong scope
)
Scoped to object:
@QUICKFUNCS(
~calculatePower<int> => player(strength<int>, agility<int>) {
return (strength * 2) + agility
}
)
@DATA(
player = {
strength = 15,
agility = 10,
power = calculatePower(strength, agility) // ✅ OK - inside player object
}
)
CRITICAL: QuickFunctions run during compilation, NOT at runtime!
Source file (what you write):
@QUICKFUNCS(
~calculateBonus<int>(base) {
return base * 2
}
)
@DATA(
base_score = 100,
bonus = calculateBonus(100) // Shows intent
)
Runtime (what your code sees):
var data = Dix.Load("game.mdix").SuccessResult;
int bonus = data.Get<int>("bonus").SuccessResult; // Value is 200 (pre-computed!)The source shows calculateBonus(100) for human readability, but the runtime gets the computed value (200).
NEW in v1.0.0: Functions can now call other functions!
@QUICKFUNCS(
// Helper function
~square<int>(x) {
return x * x
}
// Uses helper function
~pythagorean<float>(a, b) {
a_squared = square(a)
b_squared = square(b)
c_squared = a_squared + b_squared
return Math.sqrt(c_squared)
}
// Chain of calls
~calculateTotal<float>(base<float>, tax_rate<float>) {
tax = base * tax_rate
return base + tax
}
~calculateWithDiscount<float>(base<float>, discount<float>, tax_rate<float>) {
discounted = base - (base * discount)
return calculateTotal(discounted, tax_rate)
}
)
@DATA(
// Direct calls
result1 = pythagorean(3, 4), // 5.0
result2 = calculateTotal(100, 0.15), // 115.0
result3 = calculateWithDiscount(100, 0.1, 0.15) // 103.5
)
Rules:
- ✅ Functions can call other functions
- ✅ No recursion (prevents infinite loops)
- ✅ Functions execute in order (no forward references needed)
- ❌ Cannot call same function recursively
DixScript provides extensive built-in functions for common operations.
@QUICKFUNCS(
~calculateDistance<float>(x1, y1, x2, y2) {
dx = x2 - x1
dy = y2 - y1
dist_squared = (dx * dx) + (dy * dy)
return Math.sqrt(dist_squared)
}
~clampValue<int>(value, min, max) {
return Math.clamp(value, min, max)
}
~roundPrice<float>(price) {
return Math.round(price * 100) / 100
}
)
@DATA(
distance = calculateDistance(0, 0, 3, 4), // 5.0
clamped = clampValue(150, 0, 100), // 100
rounded = roundPrice(19.996) // 20.0
)
@QUICKFUNCS(
~formatTimestamp<string>() {
now = DateTime.now()
return DateTime.format(now, "yyyy-MM-dd HH:mm:ss")
}
~daysUntil<int>(target_date<date>) {
today = DateTime.today()
return Math.round(DateTime.subtract(target_date, today))
}
)
@DATA(
compiled_at = formatTimestamp(),
release_date = 2025-12-31,
days_remaining = daysUntil(release_date)
)
@QUICKFUNCS(
~topThreeScores<int>(scores<array>) {
sorted = Array.sort(scores)
reversed = Array.reverse(sorted)
top_three = Array.slice(reversed, 0, 3)
return Math.round(Array.sum(top_three))
}
~createRange<array>(start, end) {
return Array.range(start, end)
}
)
@DATA(
scores:: 50, 20, 80, 10, 40, 90, 30,
top_three_sum = topThreeScores(scores), // 220 (90+80+50)
numbers = createRange(1, 10) // [1,2,3,4,5,6,7,8,9,10]
)
@QUICKFUNCS(
~formatName<string>(first, last) {
return $"{first.toUpper()} {last.toUpper()}"
}
~validateEmail<bool>(email) {
has_at = email.contains("@")
has_dot = email.contains(".")
return has_at && has_dot
}
)
@DATA(
full_name = formatName("john", "doe"), // "JOHN DOE"
email_valid = validateEmail("[email protected]") // true
)
@QUICKFUNCS(
~rollDice<int>(sides) {
return Random.range(1, sides)
}
~selectRandom<string>(options<array>) {
return Random.choice(options)
}
)
@DATA(
dice_roll = rollDice(20), // Random 1-20
options:: "option1", "option2", "option3",
selected = selectRandom(options) // Random choice
)
Without QuickFunctions (JSON):
{
"enemies": [
{"name": "Goblin", "health": 50, "damage": 10, "armor": 5, "xp": 25, "gold": 12},
{"name": "Orc", "health": 100, "damage": 20, "armor": 10, "xp": 50, "gold": 25},
{"name": "Troll", "health": 200, "damage": 40, "armor": 20, "xp": 100, "gold": 50},
{"name": "Dragon", "health": 500, "damage": 100, "armor": 50, "xp": 500, "gold": 250}
]
}Size: 350 characters (lots of duplication)
With QuickFunctions (DixScript):
@QUICKFUNCS(
~createEnemy<object>(name, health, damage) {
return {
name = name,
health = health,
damage = damage,
armor = health / 10, // Computed!
xp = health / 2, // Computed!
gold = health / 4 // Computed!
}
}
)
@DATA(
enemies::
createEnemy("Goblin", 50, 10),
createEnemy("Orc", 100, 20),
createEnemy("Troll", 200, 40),
createEnemy("Dragon", 500, 100)
)
Size: 231 characters (34% smaller!)
Bonus: One source of truth for formulas!
Without QuickFunctions:
{
"endpoints": [
{"path": "/api/v2/users", "method": "GET", "auth": true, "rate_limit": 100},
{"path": "/api/v2/users", "method": "POST", "auth": true, "rate_limit": 50},
{"path": "/api/v2/products", "method": "GET", "auth": false, "rate_limit": 200},
{"path": "/api/v2/products", "method": "POST", "auth": true, "rate_limit": 50},
{"path": "/api/v2/orders", "method": "GET", "auth": true, "rate_limit": 100},
{"path": "/api/v2/orders", "method": "POST", "auth": true, "rate_limit": 50}
]
}Size: 450 characters
With QuickFunctions:
@ENUMS(
HttpMethod { GET = 1, POST = 2, PUT = 3, DELETE = 4 }
)
@QUICKFUNCS(
~endpoint<object>(resource, method<enum>, auth) {
rate_limit = method == HttpMethod.GET ? 200 : 50
return {
path = $"/api/v2/{resource}",
method = method,
auth = auth,
rate_limit = rate_limit
}
}
)
@DATA(
api_version = 2
endpoints::
endpoint("users", HttpMethod.GET, true),
endpoint("users", HttpMethod.POST, true),
endpoint("products", HttpMethod.GET, false),
endpoint("products", HttpMethod.POST, true),
endpoint("orders", HttpMethod.GET, true),
endpoint("orders", HttpMethod.POST, true)
)
Size: 295 characters (34% smaller!)
Bonus: Change API version in one place!
Without QuickFunctions (massive duplication):
{
"dev": {
"database": {"host": "dev-db.local", "port": 5432, "pool": 10, "timeout": 5000},
"redis": {"host": "dev-cache.local", "port": 6379, "timeout": 3000},
"api": {"host": "dev-api.local", "port": 8080, "timeout": 30000}
},
"staging": {
"database": {"host": "staging-db.local", "port": 5432, "pool": 25, "timeout": 5000},
"redis": {"host": "staging-cache.local", "port": 6379, "timeout": 3000},
"api": {"host": "staging-api.local", "port": 8080, "timeout": 30000}
},
"prod": {
"database": {"host": "prod-db.company.com", "port": 5432, "pool": 50, "timeout": 5000},
"redis": {"host": "prod-cache.company.com", "port": 6379, "timeout": 3000},
"api": {"host": "prod-api.company.com", "port": 8080, "timeout": 30000}
}
}Size: 650 characters (lots of repeated structure)
With QuickFunctions:
@ENUMS(
Environment { DEV = 1, STAGING = 2, PROD = 3 }
)
@QUICKFUNCS(
~dbConfig<object>(env<enum>, suffix) {
pool_size = env == Environment.DEV ? 10 :
env == Environment.STAGING ? 25 : 50
return {
host = $"{suffix}-db.local",
port = 5432,
pool = pool_size,
timeout = 5000
}
}
~redisConfig<object>(suffix) {
return {
host = $"{suffix}-cache.local",
port = 6379,
timeout = 3000
}
}
~apiConfig<object>(suffix) {
return {
host = $"{suffix}-api.local",
port = 8080,
timeout = 30000
}
}
)
@DATA(
dev = {
database = dbConfig(Environment.DEV, "dev"),
redis = redisConfig("dev"),
api = apiConfig("dev")
},
staging = {
database = dbConfig(Environment.STAGING, "staging"),
redis = redisConfig("staging"),
api = apiConfig("staging")
},
prod = {
database = dbConfig(Environment.PROD, "prod"),
redis = redisConfig("prod"),
api = apiConfig("prod")
}
)
Size: 438 characters (33% smaller!)
Bonus: Change timeout in one place, applies everywhere!
@QUICKFUNCS(
~applyDiscount<float>(price, discount) {
return price - (price * discount)
}
~addTax<float>(price, tax_rate) {
return price + (price * tax_rate)
}
~calculateFinalPrice<float>(base, discount, tax_rate) {
discounted = applyDiscount(base, discount)
return addTax(discounted, tax_rate)
}
)
@DATA(
base_price = 100.0,
discount = 0.1,
tax = 0.15,
final_price = calculateFinalPrice(base_price, discount, tax) // 103.5
)
@ENUMS(
Difficulty { EASY = 1, NORMAL = 2, HARD = 3, EXTREME = 5 }
)
@QUICKFUNCS(
~calculateDamage<int>(base, difficulty<enum>) {
multiplier = difficulty == Difficulty.EASY ? 0.5 :
difficulty == Difficulty.NORMAL ? 1.0 :
difficulty == Difficulty.HARD ? 1.5 : 2.0
return Math.round(base * multiplier)
}
~calculateReward<int>(base, difficulty<enum>) {
return base * difficulty // Uses enum numeric value directly
}
)
@DATA(
base_damage = 50
combat.easy:
damage = calculateDamage(base_damage, Difficulty.EASY),
reward = calculateReward(10, Difficulty.EASY)
combat.hard:
damage = calculateDamage(base_damage, Difficulty.HARD),
reward = calculateReward(10, Difficulty.HARD)
)
@QUICKFUNCS(
~createPlayer<object>(name, class_name, level) {
base_health = 100
base_mana = 50
health_multiplier = class_name == "Warrior" ? 1.5 :
class_name == "Mage" ? 0.8 : 1.0
mana_multiplier = class_name == "Mage" ? 2.0 :
class_name == "Warrior" ? 0.5 : 1.0
return {
name = name,
class = class_name,
level = level,
health = Math.round(base_health * health_multiplier * level),
mana = Math.round(base_mana * mana_multiplier * level),
xp_required = level * 100
}
}
)
@DATA(
players::
createPlayer("Aragorn", "Warrior", 10),
createPlayer("Gandalf", "Mage", 15),
createPlayer("Legolas", "Ranger", 12)
)
@QUICKFUNCS(
~buildConnectionString<string>(host, port, database, user) {
return $"Server={host};Port={port};Database={database};User={user}"
}
~formatLogMessage<string>(level, message) {
timestamp = DateTime.format(DateTime.now(), "yyyy-MM-dd HH:mm:ss")
return $"[{timestamp}] [{level}] {message}"
}
)
@DATA(
connection_string = buildConnectionString("localhost", 5432, "mydb", "admin"),
startup_log = formatLogMessage("INFO", "Application started")
)
- ✅ Call other QuickFunctions (NEW!)
- ✅ Access function parameters
- ✅ Use @ENUMS values
- ✅ Call built-in static objects (Math, DateTime, Array, Random, Enum)
- ✅ Use instance methods (string.toUpper(), array.sort(), etc.)
- ✅ Create local variables
- ✅ Use conditional expressions (ternary operator)
- ✅ Perform arithmetic and logical operations
- ✅ String interpolation
- ✅ Build objects and arrays
- ❌ Recursion (call themselves)
- ❌ Access @CONFIG values
- ❌ Access @DATA values from outside function scope
- ❌ Access external state
- ❌ Have side effects (pure functions only)
- ❌ Return void or null (must return a value)
// ✅ Good: Simple and clear
@QUICKFUNCS(
~add<int>(a, b) {
return a + b
}
)
// ✅ Good: Scoped when it makes sense
@QUICKFUNCS(
~validatePort<bool> => server.config(port) {
return port > 1024 && port < 65536
}
)
// ✅ Good: Type annotations for enums and complex types
@QUICKFUNCS(
~calculateDamage<int>(base<int>, difficulty<enum>) {
// ... implementation
}
)
// ✅ Good: Break complex logic into smaller functions
@QUICKFUNCS(
~applyDiscount<float>(price, discount) {
return price - (price * discount)
}
~addTax<float>(price, rate) {
return price + (price * rate)
}
~calculateTotal<float>(price, discount, tax) {
discounted = applyDiscount(price, discount)
return addTax(discounted, tax)
}
)
// ❌ Bad: Vague names
~calc<float>(x, y) { return x * y }
// ✅ Good: Clear purpose
~calculateTotalPrice<float>(basePrice, taxRate) {
return basePrice * (1.0 + taxRate)
}
Continue to Part 4: DLM, Security & Result Types to learn about:
-
@DLM section (compression, encryption, auditing)
-
@SECURITY section (auto-generation, password/keyfile modes)
-
Result<TSuccess, TError> class (railway-oriented programming)
-
Error handling throughout the compilation pipeline
-
Binary serialization
-
Complete compilation flow
DLM (Data Lifecycle Modules) are optional processing steps that run during compilation. Think of them as a pipeline that transforms your data.
DCompressor - Compression (reduce file size):
DCompressor.gzip- Fast, good ratio (recommended)DCompressor.bzip2- Slower, better ratioDCompressor.lzma- Slowest, best ratio
DAuditor - Audit trail generation (compliance):
DAuditor.diy- Simple text logDAuditor.enhanced- Comprehensive DixScript-formatted audit trail
DEncryptor - Encryption (requires @SECURITY):
DEncryptor.xor- LOW security (obfuscation only, testing)DEncryptor.aes128- MEDIUM security (AES-128-GCM)DEncryptor.aes256- HIGH security (AES-256-GCM, recommended for production)DEncryptor.chacha20- HIGH security (ChaCha20-Poly1305, modern)
@DLM(
Module1.subtype,
Module2.subtype,
Module3
)
Modules ALWAYS execute in this priority order (regardless of how you declare them):
Priority 1: DAuditor (start tracking)
Priority 2: DCompressor (compress data)
Priority 3: DEncryptor (encrypt compressed data)
Reversal Order (when loading):
Step 1: DEncryptor (decrypt first)
Step 2: DCompressor (decompress second)
Step 3: Parse data (load into memory)
| Modules Used | .mdix.enc | .mdix.key | .mdix.au |
|---|---|---|---|
| None | ❌ | ❌ | ❌ |
| DCompressor | ✅ | ✅ | ❌ |
| DEncryptor | ✅ | ✅ | ❌ |
| DAuditor | ❌ | ❌ | ✅ |
| Compressor + Encryptor | ✅ | ✅ | ❌ |
| All three | ✅ | ✅ | ✅ |
Note: .mdix.key is ALWAYS generated when using DCompressor or DEncryptor (contains pipeline metadata).
@DLM(DCompressor.gzip)
@DATA(
large_config = {
// ... lots of data ...
}
)
Compilation:
dixscript compile config.mdix
✅ Compiled successfully
📦 Output: config.mdix.enc (compressed)
🔑 Key file: config.mdix.key (pipeline metadata)Output Files:
config.mdix.enc- Compressed data (40-60% smaller)config.mdix.key- Pipeline metadata (compression algorithm info)
@DLM(DEncryptor.aes256)
@SECURITY(
encryption -> {
mode = "password",
algorithm = "aes256-gcm"
}
)
@DATA(
api_key = "secret123",
database_password = "password456"
)
Compilation:
dixscript compile secrets.mdix --password
Enter password: ********
Confirm password: ********
✅ Compiled successfully
🔒 Output: secrets.mdix.enc (encrypted)
🔑 Key file: secrets.mdix.key (KDF params, NO password stored!)Output Files:
secrets.mdix.enc- Encrypted datasecrets.mdix.key- KDF parameters (salt, IV, algorithm settings)
@DLM(
DAuditor.enhanced,
DCompressor.gzip,
DEncryptor.aes256
)
@SECURITY(
encryption -> {
mode = "keyfile",
algorithm = "aes256-gcm"
},
keystore -> {
auto_generate = true,
backup_count = 3
}
)
@DATA(
sensitive_data = "..."
)
Compilation:
dixscript compile production.mdix
✅ Compiled successfully
📦 Output: production.mdix.enc (compressed then encrypted)
🔑 Key file: production.mdix.key ⚠️ KEEP SECRET!
🔑 Backup: production.mdix.key.backup_20250115_103045
🔑 Backup: production.mdix.key.backup_20250115_094530
🔑 Backup: production.mdix.key.backup_20250115_081520
📋 Audit: production.mdix.au (comprehensive audit trail)Output Files:
production.mdix.enc- Compressed then encryptedproduction.mdix.key- Encryption key + pipeline metadata (SECRET!)production.mdix.key.backup_*- Backup keys (3 most recent)production.mdix.au- Audit trail (DixScript format)
Required when using DEncryptor. If missing, DixScript auto-generates defaults.
If you forget @SECURITY:
@DLM(DEncryptor.aes256)
@DATA(
api_key = "secret"
)
// Missing @SECURITY - DixScript adds it automatically!
Auto-generated @SECURITY:
@SECURITY(
encryption -> {
mode = "keyfile",
algorithm = "aes256-gcm",
key_length = 32
},
validation -> {
checksum_algorithm = "sha256",
auth_tag_length = 128,
hmac_algorithm = "hmac-sha256"
},
keystore -> {
auto_generate = true,
backup_count = 3,
backup_naming = "timestamp"
}
)
User provides password at compile-time. Key derived using Argon2id.
@DLM(DEncryptor.aes256)
@SECURITY(
encryption -> {
mode = "password",
algorithm = "aes256-gcm",
kdf = "argon2id",
kdf_memory = 65536,
kdf_iterations = 3,
kdf_parallelism = 4
}
)
@DATA(
api_key = "secret123"
)
Advantages:
- ✅ No key file to manage
- ✅ User remembers password
- ✅ Can't decrypt without password
Disadvantages:
⚠️ User must remember password⚠️ Password must be entered for every decryption⚠️ Slower (KDF computation takes ~500-2000ms)
Compilation:
dixscript compile secrets.mdix --password
Enter password: ********
Confirm password: ********
✅ Compiled successfully
🔒 secrets.mdix.enc (encrypted)
🔑 secrets.mdix.key (NO password stored - only KDF params!)Key File Contains:
- ✅ KDF parameters (algorithm, memory, iterations)
- ✅ Salt (random, non-secret)
- ✅ IV (random, non-secret)
- ✅ Validation checksums
- ❌ NO PASSWORD (password never stored anywhere!)
Loading:
var result = Dix.LoadEncWithPassword("secrets.mdix.enc", "MyPassword123");
result.Match(
data => Console.WriteLine("Decrypted successfully!"),
error => Console.WriteLine($"Failed: {error}")
);System generates random encryption key automatically.
@DLM(DEncryptor.aes256)
@SECURITY(
encryption -> {
mode = "keyfile",
algorithm = "aes256-gcm"
},
keystore -> {
auto_generate = true,
backup_count = 3
}
)
@DATA(
api_key = "secret123"
)
Advantages:
- ✅ No password to remember
- ✅ Faster decryption (no KDF, ~10-50ms)
- ✅ Automatic key backup
Disadvantages:
⚠️ Must protect .mdix.key file (contains secret key!)⚠️ If key file lost, data cannot be decrypted
Compilation:
dixscript compile production.mdix
✅ Compiled successfully
🔒 production.mdix.enc (encrypted)
🔑 production.mdix.key ⚠️ KEEP SECRET!
🔑 Backups created (3 most recent)Key File Contains:
⚠️ Encryption key (256-bit random key - SECRET!)- ✅ IV (random, non-secret)
- ✅ Validation checksums
- ✅ Pipeline metadata
🔒 CRITICAL: Protect .mdix.key file!
# Set file permissions (Linux/macOS)
chmod 600 production.mdix.key
# Store backups in secure vault
# Never commit to version control!
echo "*.mdix.key" >> .gitignoreLoading:
// Option 1: Auto-detect key file
var result = Dix.LoadEnc("production.mdix.enc");
// Option 2: Explicit key file path
var result = Dix.LoadEncWithKeyFile("production.mdix.enc", "production.mdix.key");
// Option 3: Key from vault (requires acknowledgment)
var options = new DixLoadOptions {
KeyFileContent = vaultKeyContent,
AllowDirectKeyContent = true
};
var result = Dix.LoadEnc("production.mdix.enc", options);DixScript uses railway-oriented programming. Functions return Result<T, string> instead of throwing exceptions.
Traditional exception-based code:
// ❌ Can throw at runtime - easy to forget error handling
var data = Dix.Load("config.mdix"); // Throws if file missing!
int port = data.Get<int>("port"); // Throws if key missing!Result-based code:
// ✅ Forces you to handle errors
var result = Dix.Load("config.mdix");
if (!result.IsSuccess) {
Console.WriteLine($"Error: {result.Error}");
return;
}
var data = result.SuccessResult;Pattern 1: IsSuccess Check
var result = Dix.Load("config.mdix");
if (result.IsSuccess) {
var data = result.SuccessResult;
Console.WriteLine("Loaded successfully!");
} else {
Console.WriteLine($"Error: {result.Error}");
}Pattern 2: Match (Recommended)
var result = Dix.Load("config.mdix");
result.Match(
success => Console.WriteLine($"Loaded: {success.Version}"),
error => Console.WriteLine($"Failed: {error}")
);Pattern 3: UnwrapOr (Safe Default)
var port = Dix.Load("config.mdix")
.Map(data => data.Get<int>("port").UnwrapOr(8080))
.UnwrapOr(8080);
Console.WriteLine($"Port: {port}"); // Never crashes!Map - Transform success value:
var port = Dix.Load("config.mdix")
.Map(data => data.Get<int>("port"))
.UnwrapOr(8080);AndThen - Chain operations that return Result:
var result = Dix.Load("config.mdix")
.AndThen(data => data.Get<string>("database.host"))
.AndThen(host => ValidateHost(host));
result.Match(
validHost => Console.WriteLine($"Connected to {validHost}"),
error => Console.WriteLine($"Failed: {error}")
);Tap - Side effects without changing value:
var result = Dix.Load("config.mdix")
.Tap(data => Console.WriteLine("Loaded successfully!"))
.TapError(error => Console.WriteLine($"Failed: {error}"))
.Map(data => data.Get<int>("port"));Or - Fallback to alternative:
var data = Dix.Load("primary.mdix")
.Or(Dix.Load("backup.mdix"))
.Or(Dix.Load("default.mdix"))
.SuccessResult;using DixScript.Runtime;
public class ConfigLoader
{
public Result<AppConfig, string> LoadConfig(string path)
{
return Dix.Load(path)
.AndThen(ValidateConfig)
.Map(BuildAppConfig)
.Tap(config => Console.WriteLine($"Config loaded: {config.AppName}"))
.TapError(error => Console.WriteLine($"Config error: {error}"));
}
private Result<DixData, string> ValidateConfig(DixData data)
{
if (!data.Exists("app_name"))
return Result<DixData, string>.Err("Missing app_name");
if (!data.Exists("version"))
return Result<DixData, string>.Err("Missing version");
return Result<DixData, string>.Ok(data);
}
private AppConfig BuildAppConfig(DixData data)
{
return new AppConfig {
AppName = data.Get<string>("app_name").UnwrapOr("Unknown"),
Version = data.Get<string>("version").UnwrapOr("1.0.0"),
Port = data.Get<int>("port").UnwrapOr(8080),
Debug = data.Get<bool>("debug").UnwrapOr(false)
};
}
}| Method | Description | Example |
|---|---|---|
IsSuccess |
Check if success | if (result.IsSuccess) { } |
IsFailure |
Check if error | if (result.IsFailure) { } |
SuccessResult |
Get success value | var data = result.SuccessResult |
Error |
Get error value | var error = result.Error |
Match<T> |
Pattern match | result.Match(ok => ..., err => ...) |
Map<T> |
Transform success | .Map(data => data.Get<int>("port")) |
AndThen<T> |
Chain Results | .AndThen(data => Validate(data)) |
UnwrapOr<T> |
Get or default | .UnwrapOr(8080) |
UnwrapOrElse<T> |
Get or compute | .UnwrapOrElse(err => 8080) |
Tap |
Side effect (success) | .Tap(data => Log(data)) |
TapError |
Side effect (error) | .TapError(err => Log(err)) |
Or |
Fallback result | .Or(backupResult) |
Ensure |
Validate | .Ensure(data => data != null, "Null") |
DixScript has a comprehensive error system that tracks errors at every stage.
Lexical Errors - Tokenization problems:
// Invalid hex color
color = #GGGGGG // ❌ Invalid hex digits
// Unterminated string
text = "hello // ❌ Missing closing quote
Parse Errors - Syntax problems:
// Missing arrow in CONFIG
@CONFIG(
version "1.0.0" // ❌ Missing ->
)
// Mixed ordering in DATA
@DATA(
server: host = "localhost"
name = "MyApp" // ❌ Flat after grouped
)
Semantic Errors - Logic problems:
@QUICKFUNCS(
~calculate<int>(x) {
return undefined_var // ❌ Undefined variable
}
)
@DATA(
result = nonexistent_function() // ❌ Function not found
)
Value Resolution Errors - Runtime execution problems:
@QUICKFUNCS(
~divide<int>(a, b) {
return a / b // ❌ Division by zero at compile time
}
)
@DATA(
result = divide(10, 0) // Error during compilation
)
Configured in @CONFIG:
@CONFIG(
error_handling -> "halt" // Stop on first error
// error_handling -> "continue" // Find all errors
// error_handling -> "recover" // Try to fix errors
)
halt (default, recommended for production):
✅ Fail fast
✅ Strict validation
✅ Best for production builds
continue (recommended for development):
✅ Report all errors at once
✅ See complete error list
✅ Best for debugging
recover (permissive, for migration):
✅ Try to recover from errors
✅ Insert sensible defaults
⚠️ May hide real issues
dixscript compile broken.mdix
❌ Compilation failed
[DX003L15C5] Parse Error: Unexpected token
Location: Line 15, Column 5
Message: Expected ',' but found '}'
Suggestion: Add comma between entries
Quick Fixes:
- Insert comma after previous entry
- Check if entry is complete
[DXSEM001L20C10] Semantic Error: Undefined reference
Location: Line 20, Column 10
Section: QUICKFUNCS
Message: Variable 'undefined_var' not found
Suggestion: Check variable name spelling
Quick Fixes:
- Define variable before use
- Check if variable is in scope
Total errors: 2
Compilation time: 45msSource .mdix → Compilation → Output files
↓
[7 Major Phases]
↓
.mdix.enc + .mdix.key + .mdix.au
Phase 1: Configuration Handling
1. Read source file
2. Look for @CONFIG section
3. Parse configuration settings
4. Apply to ErrorManager and compiler
5. Set error handling strategy
Phase 2: Lexical Analysis
1. Tokenize source text
2. Recognize keywords, identifiers, literals
3. Detect section boundaries
4. Handle comments
5. Produce token stream
Phase 3: Syntax Analysis (Parsing)
1. Parse token stream into AST
2. Section-specific parsers:
- ConfigSectionParser
- DLMSectionParser
- EnumsSectionParser
- QuickFunctionsSectionParser
- DataSectionParser
- SecuritySectionParser
3. Build Abstract Syntax Tree (AST)
Phase 4: Semantic Analysis
1. Build symbol table (enums, functions, variables)
2. Type checking
3. Scope validation
4. Cross-section analysis:
- EnumsSectionAnalyzer
- QuickFuncsSectionAnalyzer
- DataSectionAnalyzer
- DLMSectionAnalyzer
- SecuritySectionAnalyzer
5. Detect cycles, validate function calls
Phase 5: AST Enhancement
1. Type inference for untyped variables
2. Fill in default values
3. Resolve QuickFunction parameter defaults
4. Add missing type annotations
Phase 6: Value Resolution
1. Execute QuickFunctions at compile-time
2. Resolve function calls in DATA section
3. Compute all expressions
4. Produce final values
5. Replace function calls with computed results
Phase 7: Output Generation
If NO DLM modules:
→ No output files generated
→ AST available in memory only
If DLM modules present:
7a. Binary Serialization
- Convert AST to efficient binary format
- Add headers, checksums, metadata
7b. DLM Pipeline Execution
Priority 1: DAuditor (if present)
- Track compilation events
- Generate audit trail
- Write .mdix.au file
Priority 2: DCompressor (if present)
- Compress binary data
- Algorithm: gzip/bzip2/lzma
Priority 3: DEncryptor (if present)
- Check/generate @SECURITY section
- Password mode: Prompt for password, derive key via Argon2id
- Keyfile mode: Generate random 256-bit key
- Encrypt data using AES-256-GCM
- Generate backups if configured
7c. Write Output Files
- Write .mdix.enc (final processed data)
- Write .mdix.key (pipeline metadata + keys)
- Write .mdix.au (if auditor enabled)
┌──────────────────────────────────────────────────────┐
│ Phase 1: Configuration Handling │
│ • Parse @CONFIG section │
│ • Apply error handling strategy │
└─────────────────┬────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────┐
│ Phase 2: Lexical Analysis (Lexer) │
│ • Tokenize source text │
│ • Identify keywords, literals, symbols │
│ • Detect section boundaries │
└─────────────────┬────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────┐
│ Phase 3: Syntax Analysis (Parser) │
│ • Build Abstract Syntax Tree (AST) │
│ • Section-specific parsing │
│ • Validate grammar rules │
└─────────────────┬────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────┐
│ Phase 4: Semantic Analysis │
│ • Build symbol table │
│ • Type checking │
│ • Scope validation │
│ • Cross-section analysis │
└─────────────────┬────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────┐
│ Phase 5: AST Enhancement │
│ • Type inference │
│ • Fill defaults │
│ • Resolve parameter defaults │
└─────────────────┬────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────┐
│ Phase 6: Value Resolution │
│ • Execute QuickFunctions (compile-time) │
│ • Resolve all expressions │
│ • Compute final values │
└─────────────────┬────────────────────────────────────┘
↓
┌─────────┴─────────┐
│ DLM Modules? │
└─────────┬─────────┘
│
┌─────────┴─────────┐
│ YES │ NO
↓ ↓
┌───────────────────┐ ┌────────────────┐
│ Phase 7a: │ │ Done! │
│ Binary │ │ (AST in memory)│
│ Serialization │ └────────────────┘
└─────────┬─────────┘
↓
┌───────────────────────────────────────────┐
│ Phase 7b: DLM Pipeline │
│ │
│ Priority 1: DAuditor │
│ → Generate audit trail │
│ → Write .mdix.au │
│ │
│ Priority 2: DCompressor │
│ → Compress binary data │
│ → gzip/bzip2/lzma │
│ │
│ Priority 3: DEncryptor │
│ → Check/generate @SECURITY │
│ → Derive/generate keys │
│ → Encrypt with AES-256-GCM │
│ → Generate key backups │
└─────────┬─────────────────────────────────┘
↓
┌───────────────────────────────────────────┐
│ Phase 7c: Write Output Files │
│ • .mdix.enc (processed data) │
│ • .mdix.key (keys + metadata) │
│ • .mdix.au (audit trail) │
│ • .mdix.key.backup_* (backups) │
└───────────────────────────────────────────┘
.mdix.enc file → Loading → DixData in memory
↓
[Reverse DLM Pipeline]
↓
Usable data
Step 1: Read Key File
1. Read .mdix.key (or auto-detect)
2. Parse pipeline metadata
3. Determine reversal order
Step 2: Decrypt (if DEncryptor used)
1. Password mode: Prompt for password, derive key
2. Keyfile mode: Extract key from .mdix.key
3. Decrypt using AES-256-GCM
4. Verify HMAC/checksums
Step 3: Decompress (if DCompressor used)
1. Identify compression algorithm
2. Decompress data
3. Verify checksums
Step 4: Deserialize
1. Read binary format
2. Reconstruct AST
3. Build DixData object
4. Make available to application
Continue to Part 5: Runtime API & Advanced Usage to learn about:
-
Complete Dix runtime API
-
DixData access patterns
-
DixDataBuilder for programmatic creation
-
Format conversion (JSON/TOML/YAML)
-
String operations (minify, compact, parse)
-
Best practices and performance tips
The Dix class is your main entry point for loading and manipulating DixScript files.
Basic Loading
using DixScript.Runtime;
// Load from file path
var result = Dix.Load("config.mdix");
result.Match(
data => Console.WriteLine($"Version: {data.Version}"),
error => Console.WriteLine($"Error: {error}")
);
// Load from string content
string mdixContent = "@DATA(name = \"MyApp\")";
var result = Dix.LoadText(mdixContent);
// Load with options
var options = new DixLoadOptions {
ValidateChecksums = true,
StrictMode = true,
CacheResults = false
};
var result = Dix.Load("config.mdix", options);Loading Encrypted Files
// Auto-detect key file (looks for config.mdix.key)
var result = Dix.LoadEnc("config.mdix.enc");
// Explicit key file path
var result = Dix.LoadEncWithKeyFile("config.mdix.enc", "config.mdix.key");
// Password-encrypted
var result = Dix.LoadEncWithPassword("secrets.mdix.enc", "MyPassword123");
// Key from vault/environment (requires acknowledgment)
var vaultKeyContent = Environment.GetEnvironmentVariable("MDIX_KEY");
var options = new DixLoadOptions {
KeyFileContent = vaultKeyContent,
AllowDirectKeyContent = true // Must explicitly allow
};
var result = Dix.LoadEnc("production.mdix.enc", options);
// URL-based key (HTTPS only)
var options = new DixLoadOptions {
KeyFileUrl = "https://secure.example.com/keys/production.key"
};
var result = Dix.LoadEnc("production.mdix.enc", options);DixLoadOptions Properties
public class DixLoadOptions
{
// Encryption options
public string KeyFilePath { get; set; }
public string KeyFileContent { get; set; }
public string KeyFileUrl { get; set; } // HTTPS only
public bool AllowDirectKeyContent { get; set; } // Must be true for KeyFileContent
// Validation options
public bool ValidateChecksums { get; set; } = true;
public bool StrictMode { get; set; } = false;
// Performance options
public bool CacheResults { get; set; } = true;
public int MaxNestingDepth { get; set; } = 5;
// Error handling
public ErrorHandlingStrategy ErrorStrategy { get; set; } = ErrorHandlingStrategy.Halt;
}Load specific sections only (performance optimization):
// Load only CONFIG section
var configResult = Dix.LoadConfigSection("config.mdix");
configResult.Match(
config => Console.WriteLine($"Version: {config["version"]}"),
error => Console.WriteLine($"Error: {error}")
);
// Load only DATA section
var dataResult = Dix.LoadDataSection("config.mdix");
// Load only ENUMS section
var enumsResult = Dix.LoadEnumsSection("config.mdix");
// Load only QUICKFUNCS section
var funcsResult = Dix.LoadQuickFuncsSection("config.mdix");
// Load only SECURITY section
var securityResult = Dix.LoadSecuritySection("config.mdix");Benefits:
- ✅ Faster loading (only parses needed sections)
- ✅ Lower memory usage
- ✅ Skip unneeded validation
Use cases:
- Quick config checks
- Extract specific data without full parse
- Validate sections independently
The DixData class provides access to loaded data with a railway-oriented Result API.
public class DixData
{
// Main data access
public dynamic Data { get; } // Raw dynamic access
// Section access
public Dictionary<string, object> Config { get; }
public Dictionary<string, Dictionary<string, int>> Enums { get; }
public Dictionary<string, object> Security { get; }
public Dictionary<string, object> DLM { get; }
// Metadata
public string Version { get; }
public DateTime CompileTime { get; }
public string SourceFile { get; }
// Statistics
public int TotalKeys { get; }
public long ByteSize { get; }
}Get - Type-safe access with Result
var data = Dix.Load("config.mdix").SuccessResult;
// Simple property
var nameResult = data.Get<string>("app_name");
nameResult.Match(
name => Console.WriteLine($"App: {name}"),
error => Console.WriteLine($"Error: {error}")
);
// Nested property (dot notation)
var hostResult = data.Get<string>("server.config.host");
// Array element by index
var firstItemResult = data.Get<string>("items[0]");
// Deep nesting
var portResult = data.Get<int>("environments.production.database.port");TryGet - Boolean success check
var data = Dix.Load("config.mdix").SuccessResult;
if (data.TryGet<int>("port", out var port)) {
Console.WriteLine($"Port: {port}");
} else {
Console.WriteLine("Port not found, using default 8080");
port = 8080;
}GetOrDefault - Direct value with fallback
var data = Dix.Load("config.mdix").SuccessResult;
// Returns value or default if missing
int port = data.GetOrDefault<int>("port", 8080);
string host = data.GetOrDefault<string>("server.host", "localhost");
bool debug = data.GetOrDefault<bool>("debug", false);
Console.WriteLine($"Server: {host}:{port} (debug: {debug})");var data = Dix.Load("config.mdix").SuccessResult;
// Check if key exists
if (data.Exists("database.host")) {
Console.WriteLine("Database configured!");
}
// Check before accessing
if (data.Exists("optional_feature.enabled")) {
bool enabled = data.Get<bool>("optional_feature.enabled").SuccessResult;
if (enabled) {
// Initialize optional feature
}
}var data = Dix.Load("config.mdix").SuccessResult;
// Get all keys at a path
var keysResult = data.GetKeys("server.config");
keysResult.Match(
keys => {
Console.WriteLine("Server config keys:");
foreach (var key in keys) {
Console.WriteLine($" - {key}");
}
},
error => Console.WriteLine($"Error: {error}")
);
// Get all top-level keys
var topLevelKeys = data.GetKeys("");SelectMany - Find all values matching a pattern:
var data = Dix.Load("game.mdix").SuccessResult;
// Find all "name" properties at any depth
var namesResult = data.SelectMany<string>("**.name");
namesResult.Match(
names => {
Console.WriteLine("All names found:");
foreach (var name in names) {
Console.WriteLine($" - {name}");
}
},
error => Console.WriteLine($"Error: {error}")
);
// Find all "port" properties in "server" section
var portsResult = data.SelectMany<int>("server.**.port");
// Find all array elements
var allItemsResult = data.SelectMany<string>("items[*]");Patterns:
**- Match any level of nesting*- Match any single key name[*]- Match all array elements[0-5]- Match array elements 0 through 5
Deserialize - Convert to C# objects:
// DixScript source
@DATA(
app_name = "MyApp",
version = "1.0.0",
server:
host = "localhost",
port = 8080,
ssl_enabled = true
)
// C# classes
public class ServerConfig
{
public string Host { get; set; }
public int Port { get; set; }
public bool SslEnabled { get; set; }
}
public class AppConfig
{
public string AppName { get; set; }
public string Version { get; set; }
public ServerConfig Server { get; set; }
}
// Deserialize
var result = Dix.Load("config.mdix")
.AndThen(data => data.Deserialize<AppConfig>());
result.Match(
config => {
Console.WriteLine($"{config.AppName} v{config.Version}");
Console.WriteLine($"Server: {config.Server.Host}:{config.Server.Port}");
Console.WriteLine($"SSL: {config.Server.SslEnabled}");
},
error => Console.WriteLine($"Failed to deserialize: {error}")
);Array Deserialization:
// DixScript
@DATA(
users::
{ id = 1, name = "Alice", admin = true },
{ id = 2, name = "Bob", admin = false },
{ id = 3, name = "Charlie", admin = true }
)
// C#
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public bool Admin { get; set; }
}
var result = Dix.Load("users.mdix")
.AndThen(data => data.Get<List<User>>("users"));
result.Match(
users => {
foreach (var user in users) {
Console.WriteLine($"{user.Id}: {user.Name} (Admin: {user.Admin})");
}
},
error => Console.WriteLine($"Error: {error}")
);Programmatically create DixScript files from C# code.
using DixScript.Runtime;
var builder = new DixDataBuilder()
.WithVersion("1.0.0")
.WithCompileTime(DateTime.UtcNow);
// Add CONFIG settings
builder.Config()
.Set("encoding", "utf-8")
.Set("debug_mode", false);
// Add DATA
builder.Data()
.WithString("app_name", "MyApp")
.WithInt("port", 8080)
.WithBool("debug", false)
.WithFloat("timeout", 30.5f);
// Build in memory
var result = builder.Build();
result.Match(
data => Console.WriteLine($"Created: {data.Version}"),
error => Console.WriteLine($"Failed: {error}")
);
// Build and save to file
var saveResult = builder.BuildAndSave("output.mdix");Objects:
var builder = new DixDataBuilder();
builder.Data()
.WithObject("server", obj => obj
.WithString("host", "localhost")
.WithInt("port", 8080)
.WithBool("ssl_enabled", true)
);
// Nested objects
builder.Data()
.WithObject("database", db => db
.WithString("host", "db.local")
.WithInt("port", 5432)
.WithObject("credentials", cred => cred
.WithString("username", "admin")
.WithString("password", "secret")
)
);Arrays:
var builder = new DixDataBuilder();
// Array of primitives
builder.Data()
.WithArray("ports", new[] { 8080, 8081, 8082 });
// Array of strings
builder.Data()
.WithArray("allowed_hosts", new[] { "localhost", "127.0.0.1", "::1" });
// Array of objects
builder.Data()
.WithArray("users", new[] {
new { id = 1, name = "Alice" },
new { id = 2, name = "Bob" }
});Enums:
var builder = new DixDataBuilder();
// Define enums
builder.Enums()
.Add("LogLevel", new Dictionary<string, int> {
{ "DEBUG", 1 },
{ "INFO", 2 },
{ "WARN", 3 },
{ "ERROR", 4 }
});
// Use enum values
builder.Data()
.WithEnum("log_level", "LogLevel.INFO");public Result<DixData, string> CreateGameConfig()
{
var builder = new DixDataBuilder()
.WithVersion("1.0.0")
.WithCompileTime(DateTime.UtcNow);
// CONFIG section
builder.Config()
.Set("encoding", "utf-8")
.Set("error_handling", "halt");
// ENUMS section
builder.Enums()
.Add("Difficulty", new Dictionary<string, int> {
{ "EASY", 1 },
{ "NORMAL", 2 },
{ "HARD", 3 }
});
// DATA section
builder.Data()
.WithString("game_name", "Epic Quest")
.WithString("version", "2.1.0")
.WithEnum("difficulty", "Difficulty.NORMAL")
.WithObject("player", player => player
.WithString("name", "Hero")
.WithInt("level", 1)
.WithInt("health", 100)
.WithInt("mana", 50)
)
.WithArray("inventory", new[] {
"Sword",
"Shield",
"Health Potion"
});
return builder.Build();
}DixScript can convert between multiple formats: JSON, TOML, YAML, XML.
FromJson:
string json = @"{
""app_name"": ""MyApp"",
""port"": 8080,
""debug"": false
}";
var result = Dix.FromJson(json);
result.Match(
data => {
// Now available as DixData
int port = data.Get<int>("port").SuccessResult;
Console.WriteLine($"Port: {port}");
},
error => Console.WriteLine($"Conversion failed: {error}")
);FromToml:
string toml = @"
app_name = 'MyApp'
port = 8080
debug = false
[server]
host = 'localhost'
ssl_enabled = true
";
var result = Dix.FromToml(toml);FromYaml:
string yaml = @"
app_name: MyApp
port: 8080
debug: false
server:
host: localhost
ssl_enabled: true
";
var result = Dix.FromYaml(yaml);FromXml:
string xml = @"
<config>
<app_name>MyApp</app_name>
<port>8080</port>
<debug>false</debug>
</config>
";
var result = Dix.FromXml(xml);ToJson:
var data = Dix.Load("config.mdix").SuccessResult;
var jsonResult = data.ToJson();
jsonResult.Match(
json => {
Console.WriteLine("JSON output:");
Console.WriteLine(json);
},
error => Console.WriteLine($"Conversion failed: {error}")
);
// With options
var options = new JsonExportOptions {
Indented = true,
IncludeNullValues = false,
CamelCase = true
};
var jsonResult = data.ToJson(options);ToToml:
var data = Dix.Load("config.mdix").SuccessResult;
var tomlResult = data.ToToml();
tomlResult.Match(
toml => File.WriteAllText("config.toml", toml),
error => Console.WriteLine($"Error: {error}")
);ToYaml:
var data = Dix.Load("config.mdix").SuccessResult;
var yamlResult = data.ToYaml();
yamlResult.Match(
yaml => File.WriteAllText("config.yaml", yaml),
error => Console.WriteLine($"Error: {error}")
);ToXml:
var data = Dix.Load("config.mdix").SuccessResult;
var xmlResult = data.ToXml();
xmlResult.Match(
xml => File.WriteAllText("config.xml", xml),
error => Console.WriteLine($"Error: {error}")
);ToMdix (back to DixScript):
var data = Dix.Load("config.mdix").SuccessResult;
// Regenerate DixScript source
var mdixResult = data.ToMdix();
mdixResult.Match(
mdix => File.WriteAllText("regenerated.mdix", mdix),
error => Console.WriteLine($"Error: {error}")
);// JSON → DixScript → Compress → Encrypt → TOML
var result = Dix.FromJson(jsonContent)
.AndThen(data => {
// Add QuickFunctions to the data
var builder = DixDataBuilder.FromExisting(data);
builder.Config()
.Set("features", "compression,encryption");
return builder.Build();
})
.AndThen(data => data.ToToml());
result.Match(
toml => Console.WriteLine("Converted successfully!"),
error => Console.WriteLine($"Conversion failed: {error}")
);string source = @"
@CONFIG(
// Application version
version -> ""1.0.0"",
# Debug mode
debug_mode -> false
)
@DATA(
app_name = ""MyApp"", // Application name
port = 8080 # Server port
)
";
var minified = Dix.Minify(source);
// Output: @CONFIG(version->"1.0.0",debug_mode->false)@DATA(app_name="MyApp",port=8080)Use cases:
- Minimize network transmission
- Reduce storage size
- Remove sensitive comments before deployment
var compacted = Dix.Compact(source);
// Output: @CONFIG(version -> "1.0.0", debug_mode -> false) @DATA(app_name = "MyApp", port = 8080)Use cases:
- Keep readable but smaller
- Log file processing
- Intermediate build step
var noComments = Dix.RemoveComments(source);
// Output preserves structure but removes // and # commentsUse cases:
- Prepare for public release
- Remove developer notes
- Clean documentation examples
// Primitive values
string intStr = Dix.Stringify(42); // "42"
string floatStr = Dix.Stringify(3.14f); // "3.14f"
string boolStr = Dix.Stringify(true); // "true"
string strStr = Dix.Stringify("hello"); // "\"hello\""
// Arrays
string arrStr = Dix.Stringify(new[] { 1, 2, 3 });
// Output: "1, 2, 3"
// Objects
var obj = new { name = "Alice", age = 30 };
string objStr = Dix.Stringify(obj);
// Output: "{ name = \"Alice\", age = 30 }"// Parse primitives
int num = Dix.Parse<int>("42");
float flt = Dix.Parse<float>("3.14f");
bool flag = Dix.Parse<bool>("true");
string text = Dix.Parse<string>("\"hello\"");
// Parse arrays
int[] arr = Dix.Parse<int[]>("1, 2, 3, 4, 5");
// Parse dates
DateTime date = Dix.Parse<DateTime>("2025-12-31");DixScript includes a command-line tool for compilation and utilities.
# Install globally via dotnet tool
dotnet tool install --global DixScript.CLI
# Verify installation
dixscript --version# Basic compilation
dixscript compile config.mdix
# With password encryption
dixscript compile secrets.mdix --password
# With specific output path
dixscript compile config.mdix --output ./build/config.mdix.enc
# Verbose output (show compilation steps)
dixscript compile config.mdix --verbose
# Watch mode (recompile on changes)
dixscript compile config.mdix --watch# Validate syntax without compiling
dixscript validate config.mdix
# Validate multiple files
dixscript validate *.mdix
# JSON output for CI/CD
dixscript validate config.mdix --format json# Convert JSON to DixScript
dixscript convert config.json --to mdix
# Convert DixScript to TOML
dixscript convert config.mdix --to toml
# Convert with output path
dixscript convert config.json --to mdix --output config.mdix# Show file info
dixscript info config.mdix
# Output:
# Version: 1.0.0
# Compile Time: 2025-01-15 10:30:45 UTC
# Sections: CONFIG, DATA, ENUMS
# Total Keys: 24
# File Size: 1.2 KB
# Compression: gzip
# Encryption: aes256-gcm# Decrypt file (auto-detect key)
dixscript decrypt config.mdix.enc
# Decrypt with explicit key file
dixscript decrypt config.mdix.enc --key config.mdix.key
# Decrypt with password
dixscript decrypt secrets.mdix.enc --password
# Decrypt and output to file
dixscript decrypt config.mdix.enc --output config.mdix# Format (prettify) DixScript file
dixscript format config.mdix
# Format in place
dixscript format config.mdix --in-place
# Format with specific style
dixscript format config.mdix --style compact// ❌ Slow: Load entire file when you only need config
var data = Dix.Load("large.mdix").SuccessResult;
string version = data.Config["version"];
// ✅ Fast: Load only CONFIG section
var config = Dix.LoadConfigSection("large.mdix").SuccessResult;
string version = config["version"];// ❌ Slow: Reload on every request
public string GetAppName() {
var data = Dix.Load("config.mdix").SuccessResult;
return data.Get<string>("app_name").SuccessResult;
}
// ✅ Fast: Load once, cache
private static readonly Lazy<DixData> _config = new Lazy<DixData>(() =>
Dix.Load("config.mdix").SuccessResult
);
public string GetAppName() {
return _config.Value.Get<string>("app_name").SuccessResult;
}// ❌ Verbose: Multiple Result unwrapping
var portResult = data.Get<int>("port");
int port = portResult.IsSuccess ? portResult.SuccessResult : 8080;
// ✅ Concise: Direct with fallback
int port = data.GetOrDefault<int>("port", 8080);// ❌ Slow: Multiple individual checks
if (data.Exists("key1")) { /* ... */ }
if (data.Exists("key2")) { /* ... */ }
if (data.Exists("key3")) { /* ... */ }
// ✅ Fast: Get all keys once, check in memory
var keys = new HashSet<string>(data.GetKeys("").SuccessResult);
if (keys.Contains("key1")) { /* ... */ }
if (keys.Contains("key2")) { /* ... */ }
if (keys.Contains("key3")) { /* ... */ }// ❌ Slow: Multiple dynamic accesses
var host = data.Get<string>("server.host").SuccessResult;
var port = data.Get<int>("server.port").SuccessResult;
var ssl = data.Get<bool>("server.ssl").SuccessResult;
// ... many more properties ...
// ✅ Fast: Single deserialization
var server = data.Deserialize<ServerConfig>().SuccessResult;
// Access: server.Host, server.Port, server.Sslvar options = new DixLoadOptions {
CacheResults = true // Cache decrypted content
};
// First load: decrypt + parse (~500ms)
var data1 = Dix.LoadEnc("large.mdix.enc", options).SuccessResult;
// Subsequent loads: use cache (~5ms)
var data2 = Dix.LoadEnc("large.mdix.enc", options).SuccessResult;// Large config file (10 MB original)
@DLM(DCompressor.gzip) // Reduces to ~3 MB
@DATA(
// ... lots of data ...
)
Benchmark results:
- No compression: 10 MB → 50ms load time
- Gzip compression: 3 MB → 25ms load time (faster despite decompression!)
public class ConfigManager
{
private readonly Dictionary<string, DixData> _configs = new();
private readonly string _environment;
public ConfigManager(string environment)
{
_environment = environment;
LoadConfigs();
}
private void LoadConfigs()
{
// Load base config
var baseResult = Dix.Load("config.base.mdix");
baseResult.Match(
data => _configs["base"] = data,
error => throw new Exception($"Failed to load base config: {error}")
);
// Load environment-specific config
var envFile = $"config.{_environment}.mdix";
if (File.Exists(envFile))
{
var envResult = Dix.LoadEnc(envFile);
envResult.Match(
data => _configs["env"] = data,
error => Console.WriteLine($"Warning: {error}")
);
}
}
public T Get<T>(string path, T defaultValue = default)
{
// Try environment-specific first, then base
if (_configs.TryGetValue("env", out var envConfig))
{
var value = envConfig.GetOrDefault<T>(path, defaultValue);
if (!EqualityComparer<T>.Default.Equals(value, defaultValue))
return value;
}
return _configs["base"].GetOrDefault<T>(path, defaultValue);
}
}
// Usage
var config = new ConfigManager("production");
string dbHost = config.Get<string>("database.host", "localhost");
int dbPort = config.Get<int>("database.port", 5432);public class FeatureFlags
{
private readonly DixData _data;
public FeatureFlags(string configPath)
{
_data = Dix.Load(configPath).SuccessResult;
}
public bool IsEnabled(string featureName)
{
var path = $"features.{featureName}.enabled";
return _data.GetOrDefault<bool>(path, false);
}
public T GetFeatureConfig<T>(string featureName, string configKey, T defaultValue)
{
var path = $"features.{featureName}.config.{configKey}";
return _data.GetOrDefault<T>(path, defaultValue);
}
public List<string> GetEnabledFeatures()
{
var keysResult = _data.GetKeys("features");
if (!keysResult.IsSuccess)
return new List<string>();
var features = new List<string>();
foreach (var key in keysResult.SuccessResult)
{
if (IsEnabled(key))
features.Add(key);
}
return features;
}
}
// DixScript config
@DATA(
features.new_ui: enabled = true, config: { theme = "dark", beta = true },
features.analytics: enabled = false,
features.experimental: enabled = true, config: { log_level = "debug" }
)
// Usage
var flags = new FeatureFlags("features.mdix");
if (flags.IsEnabled("new_ui")) {
string theme = flags.GetFeatureConfig<string>("new_ui", "theme", "light");
// Show new UI with theme
}
Console.WriteLine("Enabled features:");
foreach (var feature in flags.GetEnabledFeatures()) {
Console.WriteLine($" - {feature}");
}public class AssetManager
{
private readonly DixData _assets;
public AssetManager()
{
// Load compiled asset database
_assets = Dix.LoadEnc("assets.mdix.enc").SuccessResult;
}
public List<Enemy> LoadEnemies()
{
var result = _assets.SelectMany<Dictionary<string, object>>("enemies[*]");
return result.Match(
enemyDicts => enemyDicts.Select(dict => new Enemy {
Name = dict["name"].ToString(),
Health = Convert.ToInt32(dict["health"]),
Damage = Convert.ToInt32(dict["damage"]),
XP = Convert.ToInt32(dict["xp"])
}).ToList(),
error => new List<Enemy>()
);
}
public Sprite LoadSprite(string name)
{
var pathResult = _assets.Get<string>($"sprites.{name}.path");
var widthResult = _assets.Get<int>($"sprites.{name}.width");
var heightResult = _assets.Get<int>($"sprites.{name}.height");
return new Sprite {
Path = pathResult.UnwrapOr("default.png"),
Width = widthResult.UnwrapOr(32),
Height = heightResult.UnwrapOr(32)
};
}
}
// DixScript asset database
@QUICKFUNCS(
~createEnemy<object>(name, health, damage) {
return {
name = name,
health = health,
damage = damage,
armor = health / 10,
xp = health / 2
}
}
)
@DATA(
enemies::
createEnemy("Goblin", 50, 10),
createEnemy("Orc", 100, 20),
createEnemy("Dragon", 500, 100)
sprites.player: path = "sprites/player.png", width = 32, height = 48,
sprites.goblin: path = "sprites/goblin.png", width = 32, height = 32
).mdix- Source files.mdix.enc- Compiled/processed files.mdix.key- Key file (encryption keys + pipeline metadata).mdix.au- Audit trail
@CONFIG- Compiler configuration@DLM- Data Lifecycle Modules@ENUMS- Named constants@QUICKFUNCS- Compile-time functions@DATA- Actual data@SECURITY- Security configuration
Dix- Main static API for loading/convertingDixData- Loaded data with path-based accessDixDataBuilder- Programmatic creationResult<T, E>- Railway-oriented error handlingDixLoadOptions- Configuration for loading
Dix.Load()/LoadText()/LoadEnc()DixData.Get<T>()/TryGet<T>()/GetOrDefault<T>()DixData.Exists()/GetKeys()/SelectMany<T>()DixData.Deserialize<T>()DixData.ToJson()/ToToml()/ToYaml()
You now have comprehensive knowledge of DixScript!
What makes DixScript special:
- ✅ 27-34% smaller than JSON/TOML via compile-time deduplication
- ✅ QuickFunctions eliminate repetition at compile-time
- ✅ Railway-oriented programming forces proper error handling
- ✅ Built-in encryption with AES-256-GCM
- ✅ Zero dependencies, pure C# (.NET 8+)
- ✅ Format agnostic - convert to/from JSON, TOML, YAML, XML
- ✅ Type-safe with strongly-typed deserialization
- ✅ Developer-friendly with clear error messages
When to use DixScript:
- Large configuration files with repetitive structure
- Multi-environment deployments
- Encrypted secrets management
- Game data/asset pipelines
- API configuration with many endpoints
- Any scenario where you're copying/pasting similar config blocks
Get Started:
dotnet add package DixScript.Runtime
dotnet tool install --global DixScript.CLI
dixscript compile myconfig.mdixResources:
- Documentation: https://dixscript.dev
- GitHub: https://github.com/dixscript/dixscript
- Discord: https://discord.gg/dixscript
Happy scripting! 🚀