8.2 KiB
Slang Language Compiler - AI Agent Instructions
Project Overview
Slang is a high-level programming language that compiles to IC10 assembly for the game Stationeers. The compiler is a multi-stage Rust system with a C# BepInEx mod integration layer.
Key Goal: Reduce manual IC10 assembly writing by providing C-like syntax with automatic register allocation and device abstraction.
Architecture Overview
Compilation Pipeline
The compiler follows a strict 4-stage pipeline (in rust_compiler/libs/compiler/src/v1.rs):
-
Tokenizer (libs/tokenizer/src/lib.rs) - Lexical analysis using
logoscrate- Converts source text into tokens
- Tracks line/span information for error reporting
- Supports temperature literals (c/f/k suffixes)
-
Parser (libs/parser/src/lib.rs) - AST construction
- Recursive descent parser producing
Expressiontree - Validates syntax, handles device declarations, function definitions
- Output:
Expressionenum containing tree nodes
- Recursive descent parser producing
-
Compiler (v1) (libs/compiler/src/v1.rs) - Semantic analysis & code generation
- Variable scope management and register allocation via
VariableManager - Emits IL instructions to
il::Instructions - Error types use
lsp_types::Diagnosticfor editor integration
- Variable scope management and register allocation via
-
Optimizer (libs/optimizer/src/lib.rs) - Post-generation optimization
- Currently optimizes leaf functions
- Optional pass before final output
Cross-Language Integration
- Rust Library (
slang.dll/.so): Core compiler logic viasafer-ffiC FFI bindings - C# Mod (
StationeersSlang.dll): BepInEx plugin integrating with game UI - Generated Headers (via
generate-headersbinary): Auto-generated C# bindings from Rust
Key Types & Data Flow
Expressiontree (parser) →v1::Compilerprocesses →il::InstructionsoutputInstructionNodewraps IC10 assembly with optional source span for debuggingVariableManagertracks scopes, tracks const/device/let distinctionsOperandenum represents register/literal/device-property values
Critical Workflows
Building
cd rust_compiler
# Build for both Linux and Windows targets
cargo build --release --target=x86_64-unknown-linux-gnu
cargo build --release --target=x86_64-pc-windows-gnu
# Generate C# FFI headers (requires "headers" feature)
cargo run --features headers --bin generate-headers
# Full build (run from root)
./build.sh
Testing
cd rust_compiler
# Run all tests
cargo test --package compiler --lib
# Run specific test file
cargo test --package compiler --lib tuple_literals
# Run single test
cargo test --package compiler --lib -- test::tuple_literals::test::test_tuple_literal_size_mismatch --exact --nocapture
Quick Compilation
cd rust_compiler
# Compile Slang code to IC10 using current compiler changes
echo 'let x = 5;' | cargo run --bin slang
# Or from file
cargo run --bin slang -- input.slang -o output.ic10
# Optimize the output with -z flag
cargo run --bin slang -- input.slang -o output.ic10 -z
Codebase Patterns
Test Structure
Tests follow a macro pattern in libs/compiler/src/test/mod.rs:
#[test]
fn test_name() -> Result<()> {
let output = compile!(debug "slang code here");
assert_eq!(
output,
indoc! {
"Expected IC10 output here"
}
);
Ok(())
}
compile!()macro: full pipeline from source to IC10compile!(result ...)for error checkingcompile!(debug ...)for intermediate IR inspection- Test files organize by feature:
binary_expression.rs,syscall.rs,tuple_literals.rs, etc.
Error Handling
All stages return custom Error types implementing From<lsp_types::Diagnostic>:
tokenizer::Error- Lexical errorsparser::Error<'a>- Syntax errorscompiler::Error<'a>- Semantic errors (unknown identifier, type mismatch)- Device assignment prevention:
DeviceAssignmenterror if reassigning device const
Variable Scope Management
variable_manager.rs handles:
- Tracking const vs mutable (let) distinction
- Device declarations as special scope items
- Function-local scopes with parameter handling
- Register allocation via
VariableLocation
LSP Integration
Error types implement conversion to lsp_types::Diagnostic for IDE feedback:
impl<'a> From<Error<'a>> for lsp_types::Diagnostic { ... }
This enables real-time error reporting in the Stationeers IC10 Editor mod.
Project-Specific Conventions
Tuple Destructuring
The compiler supports tuple returns and multi-assignment:
let (x, y) = func(); // TupleDeclarationExpression
(x, y) = another_func(); // TupleAssignmentExpression
Compiler validates size matching with TupleSizeMismatch error.
Device Property Access
Devices are first-class with property access:
device ac = "d0";
ac.On = true;
ac.Temperature > 20c;
Parsed as MemberAccessExpression, compiled to device I/O syscalls.
Temperature Literals
Unique language feature - automatic unit conversion at compile time:
20c → 293.15k // Celsius to Kelvin
68f → 293.15k // Fahrenheit to Kelvin
Tokenizer produces Literal::Number(Number(decimal, Some(Unit::Celsius))).
Constants are Immutable
Once declared with const, reassignment is a compile error. Device assignment prevention is critical (prevents game logic bugs).
Integration Points
C# FFI (csharp_mod/FfiGlue.cs)
- Calls Rust compiler via marshaled FFI
- Passes source code, receives IC10 output
- Marshals errors as
Diagnosticobjects
BepInEx Plugin Lifecycle
- Harmony patches for IC10 Editor integration
- Cleanup code for live-reload support (mod destruction)
- Logger integration for debug output
CI/Build Target Matrix
- Linux:
x86_64-unknown-linux-gnu - Windows:
x86_64-pc-windows-gnu(cross-compile from Linux) - Both produce dynamic libraries + CLI binary
Debugging Tips
- Print source spans:
Spantype tracks line/column for error reporting - IL inspection: Use
compile!(debug source)to view intermediate instructions - Register allocation:
VariableManagerlogs scope changes; check for conflicts - Syscall validation: parser/src/sys_call.rs lists all valid syscalls
- Tokenizer issues: Check tokenizer/src/token.rs for supported keywords/symbols
Key Files for Common Tasks
| Task | File |
|---|---|
| Add language feature | libs/parser/src/lib.rs + test in libs/compiler/src/test/ |
| Fix codegen bug | libs/compiler/src/v1.rs (~3500 lines) |
| Add syscall | libs/parser/src/sys_call.rs |
| Optimize output | libs/optimizer/src/lib.rs |
| Mod integration | csharp_mod/ |
| Language docs | docs/language-reference.md |
Dependencies to Know
logos- Tokenizer with derive macrosrust_decimal- Precise decimal arithmetic for temperature conversionsafer-ffi- Safe C FFI between Rust and C#lsp-types- Standard for editor diagnosticsthiserror- Error type derivationclap- CLI argument parsinganyhow- Error handling in main binary