"The safest room for code."
OdaLanguage is a safe, deterministic target language for AI-generated systems code. It gives AI agents a small, inspectable language that transpiles to standard C while enforcing strict semantic checks before code generation.
The goal is not to replace C as a systems substrate. The goal is to give generated systems code a safer source language with explicit memory scopes, conservative type rules, predictable lowering, and machine-readable compiler feedback that an agent can use for correction loops.
The compiler pipeline is intentionally simple:
Lexer -> Parser -> AST -> Semantic Analyzer -> C Code Generator -> native binary
| Feature | Status |
|---|---|
| C transpilation | .oda source is lowered into a single C translation unit. |
| Semi-static typing | Explicit primitive, array, function, and class annotations. |
| Null safety | Nullable values use ?; fallback expressions use ??; guard ... when unwraps nullable results. |
| Immutability | stay marks a value as immutable after initialization, including array elements. |
| RAII-style cleanup | destruct() is called automatically when generated scopes exit. |
| Ranges and loops | for-in, while, C-style for, .., ..=, step, and reversed. |
| Arrays | Dynamic-style literals, fixed-size annotations, multidimensional arrays, and new allocation. |
| Classes | Private fields with _, construct, methods, and destruct. |
| Enums | enum Name { Variant } declarations compile to standard C typedef enum. |
ref parameters |
Explicit pass-by-reference at function boundaries. |
| String interpolation | Interpolated expressions such as "sum={a+b}". |
| Pattern matching | match (value) { pattern { ... } _ { ... } } for integers, strings, and enums. |
| Explicit casts | Use expr as type or (type)expr; narrowing requires an explicit cast. |
| Unsigned integers | uint values can be written with a u suffix, such as 5u. |
| File and console I/O | readFile() and input() builtins. |
| Strict semantic checking | Semantic errors stop compilation before C generation. |
| Shared type engine | Semantic analysis and code generation use one expression type inference engine. |
| Explicit memory scopes | Block scopes isolate variables and generated RAII cleanup is tied to lexical scope exits. |
| Machine-readable diagnostics | --output-format=json emits parser and semantic errors as structured JSON. |
| AST export | export-ast serializes parsed code structure as JSON for agent analysis. |
# Transpile only
./oda transpile examples/hello.oda
# Transpile and compile
./oda build examples/hello.oda
# Transpile, compile, and run
./oda run examples/hello.oda
# Export parsed AST as JSON
./oda export-ast examples/hello.oda
# Equivalent flag form
./oda --export-ast examples/hello.oda
# Emit machine-readable diagnostics for correction loops
./oda transpile examples/hello.oda --output-format=json
# Run the compiler test suite
make test
# Run the suite with AddressSanitizer enabled for generated C binaries
make test-asanGenerated C integration tests should be run under AddressSanitizer before changes that touch code generation, RAII cleanup, arrays, strings, or scope exits are merged.
make test-asanThis target runs python -m pytest tests with ODA_TEST_CFLAGS="-fsanitize=address -g", so integration tests compile generated C with ASan instrumentation and fail if the generated binaries report memory errors. During local development, use make test-asan as the final check after make test, or run it directly when working on memory-owning language features.
Programs under tests/semantic_negative/ are expected to fail before C generation. Each file starts with an # EXPECT_ERROR: ... metadata line, and tests/test_semantic_negative.py checks that _pipeline() rejects the program with a matching semantic diagnostic.
These cases document Oda's fail-fast contract for private member access, null assignment to non-nullable variables, unsafe implicit numeric conversions, undefined names, function arity and argument type errors, missing ref, invalid guard exits, stay reassignment, and missing returns.
OdaLanguage is designed to be useful inside automated code-generation loops:
- Strict static checks: undefined variables, private member access, function call arity, argument types,
refmatching, return coverage, unsafe implicit numeric conversions, and invalid binary expressions are rejected before C is emitted. - Explicit memory scopes: lexical blocks create nested environments, RAII destructors are emitted at scope exits, and generated C avoids non-standard lifetime tricks where possible.
- Machine-readable diagnostics: parser and semantic failures can be emitted as a JSON array with
file,line,column,error_type, andmessage. - Machine-readable structure:
export-astemits the parsed AST as JSON withnode_type, source positions, declarations, statements, expressions, and type annotations.
Example diagnostic output:
./oda transpile examples/broken.oda --output-format=json[
{
"file": "examples/broken.oda",
"line": 1,
"column": 7,
"error_type": "SemanticError",
"message": "Undefined variable 'missing_value'"
}
]The examples/ directory contains small programs that double as living documentation. Golden tests ensure every .oda example transpiles, compiles with gcc -Wall -Wextra -Werror, and matches its checked-in C snapshot.
| File | Demonstrates |
|---|---|
examples/hello.oda |
Basic variables and expression interpolation. |
examples/control_flow.oda |
match, ranges, nested loops, while, and step. |
examples/arrays.oda |
Array iteration, indexing, and multidimensional arrays. |
examples/functions_ref.oda |
Functions, return values, and ref parameters. |
examples/classes_raii.oda |
Classes, private fields, constructors, methods, destructors, and RAII cleanup. |
examples/guard_io.oda |
readFile(), nullable unwrap, and guard ... when flow. |
Run any example with:
./oda run examples/control_flow.odaint a = 45
int b = 2123
print("a+b= {a+b}")
Interpolation braces accept full Oda expressions, not only variable names.
uint workers = 5u
float ratio = 3.75
int rounded = ratio as int
uint explicit_count = (uint)rounded
print("workers={workers}")
print("rounded={rounded}")
Oda keeps implicit numeric coercions conservative. Widening such as int -> float is allowed, but narrowing conversions such as float -> int or potentially unsafe conversions such as int -> uint must be written explicitly with a cast.
string command = "start"
match (command) {
"start" { print("command=start") }
"stop" { print("command=stop") }
_ { print("command=unknown") }
}
for (int i in 0..=4 step 2) {
print("i={i}")
}
enum Mode { Idle, Busy, Done }
func describe(Mode mode) {
match (mode) {
Mode.Idle { print("idle") }
Mode.Busy { print("busy") }
_ { print("done") }
}
}
Mode current = Mode.Busy
describe(current)
Enum variants are referenced as EnumName.Variant. The C generator emits a standard enum with prefixed C variant names, for example Mode_Busy, to avoid global name collisions.
int[][] matrix = [[1, 2], [3, 4]]
print("matrix[1][0]={matrix[1][0]}")
for (int[] row in matrix) {
print("row-sum={row[0] + row[1]}")
}
Array indexes must be integer expressions (int or uint). Oda rejects float or string indexes during semantic analysis instead of letting invalid C be generated.
stay arrays are fully immutable:
stay int[] nums = [1, 2, 3]
// nums[0] = 99 // SemanticError
// nums = [4, 5] // SemanticError
func bump(ref int value) {
value += 1
}
int total = 41
bump(ref total)
print("total={total}")
class Counter {
int _value
construct(int start) {
_value = start
}
func inc() {
_value += 1
}
func get() -> int {
return _value
}
destruct() {
if (_value >= 0) {
print("counter closed")
}
}
}
Counter counter = Counter(5)
counter.inc()
int current = counter.get()
print("counter now={current}")
Fields beginning with _ are private. The semantic analyzer rejects private member access from outside the class.
func load_config() {
guard string content = readFile("config.txt") else {
when (FileNotFound) {
print("config missing")
return
}
}
print(content)
}
Each when block inside a guard must leave the current scope with return, break, or continue.
OdaLanguage/
├── oda # CLI wrapper
├── src/oda/
│ ├── tokens.py # Token definitions
│ ├── lexer.py # Tokenizer
│ ├── parser.py # Recursive descent parser
│ ├── ast_nodes.py # AST dataclasses
│ ├── type_engine.py # Shared expression type inference and coercion rules
│ ├── semantic.py # Semantic analysis
│ ├── codegen.py # C code generator
│ ├── importer.py # Import resolver / unity AST builder
│ └── main.py # CLI entry point
├── examples/ # Executable language examples
├── tests/ # Unit, integration, and golden tests
└── docs/ # Language notes
- Python 3.10+
- GCC or Clang
pytestfor the test suite
OdaLanguage is experimental and under active development. The current implementation prioritizes a small, inspectable compiler pipeline and strict tests over language breadth.
