Raven is a modern general-purpose programming language and compiler inspired by the .NET Roslyn architecture.
✨ Key traits:
- Expression-first Raven syntax — explicit
val/var,match,record class, andResult/Option-centric flow - Targets the .NET runtime — compiles directly to IL and interoperates with .NET libraries
- Compiler-as-a-Service architecture — immutable, service-oriented compiler APIs
Raven is primarily a learning and exploration project, aimed at:
- Understanding modern compiler construction
- Experimenting with language design
- Providing a clean API for syntax manipulation and analysis
Raven is expression-oriented and blends functional and imperative styles. It uses () (unit) instead of void, models recoverable failure with Result<T, E> and absence with Option<T>, and uses explicit mutability (val vs var). As a .NET language, Raven interops directly with existing .NET APIs while keeping Raven-native syntax and semantics.
Type members are public by default; add access modifiers only when you intentionally narrow visibility.
import System.*
import System.Console.*
import System.Linq.*
import System.Collections.Generic.*
val plans = List<RatePlan> {
RatePlan("NorthStar", 500, 120)
RatePlan("Oceanic", 450, 150)
}
val requests = List<ShipmentRequest> {
ShipmentRequest("REQ-1001", "NorthStar", 10)
ShipmentRequest("REQ-1002", "Oceanic", 3)
}
func BuildQuoteSummary(items: IEnumerable<ShipmentRequest>, rates: IEnumerable<RatePlan>) -> Result<QuoteSummary, QuoteError> {
val request = items.FirstOrError(r => r.Id == "REQ-1002", () => QuoteError("Request not found"))?
val plan = rates.FirstOrError(r => r.Carrier == request.Carrier, () => QuoteError("Rate plan not found"))?
val total = plan.BaseCents + (request.WeightKg * plan.PerKgCents)
return Ok(QuoteSummary(request.Id, request.Carrier, total))
}
val summary = BuildQuoteSummary(requests, plans) match {
Ok(val item) => "Quote ${item.Id}: ${item.TotalCents} cents"
Error(val error) => "Quote failed: ${error.Message}"
}
WriteLine(summary)
record class ShipmentRequest(val Id: string, val Carrier: string, val WeightKg: int)
record class RatePlan(val Carrier: string, val BaseCents: int, val PerKgCents: int)
record class QuoteSummary(val Id: string, val Carrier: string, val TotalCents: int)
record class QuoteError(val Message: string)
Highlights:
Result/Optioncomposition with?propagationmatchas a first-class expressionrecord class+ promoted constructor parametersval/varmutability made explicit- Direct interop with .NET libraries and LINQ
Read the full Introduction and Getting Started for the complete flow.
Ravens are remarkable birds, known for their intelligence and adaptability.
In Old Norse mythology, ravens held significant importance as messengers of Odin. His two ravens, Huginn ("thought") and Muninn ("memory/mind"), symbolized intellect and reflection—qualities that align with the goals of this language.
The name reflects both the mythological roots and the clever traits of these birds.
Alternative names considered: Old Norse "Hrafn" or Danish "Ravn."
- Create a Programming Language — build a language from the ground up, covering design and implementation.
- Focus on Parsing & Semantics — implement parsing, binding, and analysis as the backbone of compilation.
- Serve as a Reference — provide a well-documented example for compiler enthusiasts.
- Pragmatic Scope — aim for a practical subset of Roslyn-like features, not full parity.
See the pseudo-specification here.
More samples.
- Compiler API reference: docs/compiler/api
- Example usage: Raven.Compiler project
- .NET SDK 9.0
- Optional: DocFX for docs
# Build essentials
scripts/codex-build.sh
# Run baseline tests (runtime/emission-heavy suites excluded)
scripts/test-baseline.shCompile and run a sample case:
dotnet run -f net9.0 --project src/Raven.Compiler --property WarningLevel=0 -- \
samples/cases/quote-summary-linq-result-option.rav -o /tmp/raven-sample.dll --runUseful debug flags:
-s # syntax tree
-d pretty # pretty syntax dump
-bt # binder + bound tree
--no-emit # analysis only
--run # execute after successful compileCreate and run a Raven app project:
mkdir hello-raven
cd hello-raven
# Create a project scaffold (default type: app)
dotnet run --project ../src/Raven.Compiler -- init
# Build (project files default output to ./bin)
dotnet run --project ../src/Raven.Compiler -- *.ravenproj
# Run the produced assembly
dotnet bin/Hello.dllCreate a class library scaffold instead:
dotnet run --project ../src/Raven.Compiler -- init --type classlib --name MyLibraryProject-system and NuGet details:
Command:
dotnet run --project src/Raven.Compiler -- <path-to-file> -o <output-file-path>Options:
--framework <tfm>– target framework--refs <path>– additional metadata reference (repeatable)--raven-core <path>– reference a specificRaven.Core.dll--emit-core-types-only– embed Raven core shims instead of usingRaven.Core.dll-o <path>– output assembly path-s– display the syntax tree (single file only)-ps– prints the tokens as they are being parsed-d [plain|pretty[:no-diagnostics]]– dump syntax (plainfor raw text,prettyfor highlighted syntax; append:no-diagnosticsto skip underlines, single file only)--highlight– display diagnostics with highlighted source snippets and severity-coloured underlines (covers compiler, analyzer, and emit diagnostics)-r– print the raw source (single file only)-b– print the binder tree (single file only)-bt– print the binder and bound tree (single file only)-q,--quote– print the parsed syntax as compilable C#SyntaxFactorycode using RavenQuoter defaults (includes trivia, static factory import, and named arguments)--symbols [list|hierarchy]– inspect source symbols (listdumps properties,hierarchyprints the tree)-h,--help– show help
ravc references Raven.Core.dll by default. Use --raven-core to point to a different build of Raven.Core, or --emit-core-types-only to embed shimmed core types instead of referencing the DLL.
Creating a .debug/ directory in the current or parent folder causes the
compiler to emit per-file dumps (syntax tree, highlighted syntax, raw source,
bound tree, and binder tree) into that directory. The debug options above will additionally
write to the console when compiling a single file.
⚠️ When the arguments are omitted, there is a hardcoded input file, and a hardcoded output file path (test.dll).
dotnet run --project src/Raven.Editor -- <path-to-file>When a file path is supplied, the editor opens the file and displays its name in the window title.
The Raven VS Code extension now supports F5 compile-and-debug for both single files and project files:
.ravactive file.ravenprojproject
Repository launch presets are included in .vscode/launch.json:
Raven: Compile and Debug (active file)Raven: Compile and Debug (project)
The debug flow compiles with Raven.Compiler into ${workspaceFolder}/.raven-debug, then launches dotnet <output.dll> under the debugger.
For details and configuration options (raven.compilerProjectPath, raven.languageServerPath, raven.targetFramework), see docs/compiler/raven-vscode-extension.md.
src/
Raven.CodeAnalysis/ # Compiler core: syntax, binder, semantic model, code gen
Raven.Compiler/ # Command-line compiler
Raven.CodeAnalysis.Testing/ # Diagnostic test helpers
TestDep/ # Auxiliary test project
test/ # Unit tests
samples/ # Example Raven programs and CLI demos
tools/
NodeGenerator/ # Generates syntax node code from Model.xml
Generator/ # Shared Roslyn generator framework
docs/ # Language spec & design docs
- The
RunNodeGeneratortarget inRaven.CodeAnalysis.csprojruns automatically, but if generated files are missing, run the command manually. - Generated files reside in
Syntax/generated/andSyntax/InternalSyntax/generated/— do not edit by hand. - Always run
dotnet buildanddotnet testbefore committing.
Contributions are welcome! See CONTRIBUTING.md for coding standards, git conventions, and workflow.
- Full documentation: docs/
- Unit tests for the language: Raven.CodeAnalysis.Tests
💡 Raven is a playground for exploring compilers and language design — your ideas and contributions can directly shape its evolution!