Welcome to the Vyn Programming Guide. This guide walks you through writing, building, and extending Vyn programs, from your first "Hello, Vyn!" to deep dives into the Vyn language internals and runtime. Version 0.4.4 (freedom-1.0 series) delivers a robust systems programming language with LLVM backend, native code compilation to object files, complete sized type system, pattern matching with match statements, comprehensive control flow including break/continue, resizable Vec<T> collections, unified syntax, generic function monomorphization, and comprehensive auto-serialization capabilities.
This guide is intended for systems programmers, language designers, and developers who want:
- A compact, expressive syntax for both low-level control and high-level abstractions.
- Planned fine-grained memory management options, including GC, manual free, and scoped regions.
- Built-in concurrency primitives and customizable threading templates.
- A foundation for a self-hosted compiler and hybrid VM/JIT architecture for rapid iteration and performance tuning.
Whether you're coming from C/C++, Rust, D, or other modern systems languages, you'll find Vyn's template-driven approach familiar yet uniquely powerful.
Here's a comparison of Vyn against several modern systems languages, showing key similarities and differences:
| Language | Templates / Generics | Memory Model | Concurrency | Syntax Style | Unique Feature | Comment |
|---|---|---|---|---|---|---|
| Vyn | Monomorphized templates everywhere | Ownership types (my/our/mild/their) reference counting | Async/await, planned actors, threads, channels | Indentation-based, optional braces | Planned self-hosting compiler; dual VM/JIT backend, hybrid indentation | Combines zero-cost templates with flexible memory management |
| Rust | Monomorphized generics | Ownership/borrow checker; optional Arc/Rc |
async/await, threads, channels |
C-style braces, macros | Zero-cost abstractions; strong compile-time safety | No global GC; all memory safety enforced at compile time |
| D | Runtime & compile-time templates | GC by default; @nogc for manual alloc/free |
std.concurrency fibers, threads |
C-style; mixins | Compile-time function execution (CTFE) | Blend of high-level features with systems control |
| C++ | Templates & concepts (20+) | Manual new/delete; smart pointers (unique_ptr, shared_ptr) |
Threads, coroutines (co_await) |
C-style braces | Metaprogramming via templates & concepts | Extensive ecosystem; highest portability |
| Nim | Generics + macros | GC by default; optional manual alloc |
Async (async/await), threads |
Python-like indentation | Hygienic macros; optional GC or ARC | Very concise syntax; strong metaprogramming support |
| Go | Generics (1.18+) | GC only | Goroutines, channels | C-style, minimal | CSP-style concurrency | Simple, fast compile; built-in tooling |
Note: Each language offers a different balance of safety, performance, and ergonomics. Vyn's strength lies in unifying template metaprogramming, planned flexible memory management, and a future hybrid VM/JIT in a terse, self-hosted package.
Vyn is a statically typed, template-metaprogramming language designed to compile to native code via LLVM. Its key differentiators:
- Terse Syntax: Indentation-based or bracket-based blocks, optional semicolons, clear constructs.
- Templates Everywhere: Monomorphized generics for types and functions.
- Canonical Ownership Syntax: Unified
my(expr),our(expr),soft(),view,borrowoperators for memory management. - Reference Counting with Weak References:
our<T>for shared ownership,mild<T>for weak references that don't prevent cleanup. - Concurrency Built In: Async/await, with planned actors, threads, and typed channels.
- Self-Hosting & Extensible: Planned compiler written in Vyn; add backends, macros, and modules at runtime.
Current Version: 0.4.3 (freedom-1.0 series) 🚀 AOT COMPILATION + CROSS-PLATFORM SUPPORT
# Clone and build
git clone https://github.com/rickenator/Vyn.git
cd Vyn
make -C build -j
# Run with modern test harness (391+ tests)
python3 test_harness.py --parallel --html-report --triage
# Try examples with canonical syntax
build/vyn examples/main.vyn
cd Vyn
make -C build -j
# Run your first Vyn program
echo 'main()<Int> -> { return 42 }' > hello.vyn
build/vyn hello.vyn # Returns exit code 42
# Try select expressions with pattern matching
cat > example.vyn << 'EOF'
main()<Int> -> {
numbers<Vec<Int>> = Vec::new();
numbers.push(10);
numbers.push(20);
result<Int> = select(numbers.len()) -> {
0 -> 0,
2 -> {
sum<Int> = numbers.get(0) + numbers.get(1);
pass sum
},
? -> -1
};
return result
}
EOF
build/vyn example.vyn # Returns 30
# Complex return types with auto-serialization
echo 'main()<Int,String> -> { return 42, "Hello!" }' > tuple.vyn
build/vyn tuple.vyn # Outputs: [42, "Hello!"]Vyn provides a complete compilation pipeline from source to standalone executables:
# Build executable (simplest form)
build/vyn hello.vyn --build hello
./hello # Run directly!
# Build with custom name
build/vyn program.vyn --build myapp
./myapp
# Build with optimization
build/vyn program.vyn -b myapp -O3 # Maximum optimization
# Static linking (fully standalone, no runtime dependencies)
build/vyn program.vyn -b myapp --static
# What happens:
# 1. Compiles hello.vyn → hello.o (LLVM object file)
# 2. Compiles runtime/vyn_runtime.c → vyn_runtime.o (C runtime)
# 3. Links with system linker (lld/ld) + CRT files + libc/libm
# 4. Creates executable: hello
# Verify the executable
file myapp
# Output: myapp: ELF 64-bit LSB executable, x86-64, dynamically linked, with debug_info
ldd myapp
# Output:
# linux-vdso.so.1 (0x00007fff...)
# libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f...)
# libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f...)# Compile to object file (default -O2 optimization)
build/vyn hello.vyn --compile hello.o
# Specify optimization level
build/vyn program.vyn -c program.o -O0 # No optimization (fast compile)
build/vyn program.vyn -c program.o -O1 # Basic optimization
build/vyn program.vyn -c program.o -O2 # Moderate optimization (default)
build/vyn program.vyn -c program.o -O3 # Aggressive optimization
# Verify object file was created
file hello.o
# Output: hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), with debug_info, not stripped
# Inspect symbols in object file
objdump -t hello.o | grep main
# Output: 0000000000000000 g F .text 000000000000005c mainSupports 20+ architectures out of the box:
- x86-64 (default for most systems)
- ARM/AArch64 (ARM64, Raspberry Pi, mobile)
- RISC-V (open ISA, embedded systems)
- PowerPC (servers, embedded)
- MIPS (routers, embedded)
- SPARC (Sun/Oracle systems)
- WebAssembly (browsers, serverless)
- SystemZ (IBM mainframe)
- Hexagon (Qualcomm DSP)
- LoongArch (Chinese CPUs)
- M68k (retro computing)
- Xtensa (ESP32 microcontrollers)
- AVR (Arduino)
- MSP430 (ultra-low-power MCU)
- BPF (Linux kernel filtering)
- NVPTX (NVIDIA GPUs)
- AMDGPU (AMD GPUs)
- VE (NEC Vector Engine)
- Lanai (research processor)
- XCore (XMOS processors)
Complete Compilation Features:
- ✅ Executable Generation (v0.4.4): Full build pipeline with
--buildflag - ✅ Runtime Library (v0.4.4): Automatic compilation and linking of Vyn runtime
- ✅ System Linker Integration (v0.4.4): Platform-aware linker selection (lld, ld, ld64)
- ✅ Dynamic Linking (v0.4.4): Links against system libc and libm
- ✅ Static Linking (v0.4.4): Optional
--staticflag for standalone binaries - ✅ Object File Emission (v0.4.3): AOT compilation to .o files
- ✅ Optimization Levels (v0.4.3): -O0 through -O3 with LLVM optimizations
- ✅ Cross-Compilation (v0.4.3): 20+ target architectures
- ✅ Debug Information (v0.4.3): Full DWARF debug metadata
- 🔜 Optimization Pipeline (v0.5.2): Advanced LLVM passes, LTO
- 🔜 Multi-File Compilation: Link multiple .vyn files together
- 🔜 Dynamic Libraries: Shared object (.so/.dylib) generation
- 🔜 Package Building: Full project management with dependency resolution
See: doc/MODULE_FFI_BINARY_ROADMAP.md for the complete compilation roadmap
- Template: A generic type/function parameterized by types or constants, instantiated at compile time.
- Binding Mutability: Variables are declared with unified syntax (mutable by default) or
const(immutable binding, cannot be reassigned). - Ownership Types:
my<T>: Unique ownership of dataT.our<T>: Shared (reference-counted) ownership of dataT.their<T>: Non-owning borrow/reference to dataT.mild<T>: Mild reference that can detect when target is destroyed.
- Data Mutability: Indicated by
conston the type itself (e.g.,my<T const>for unique ownership of immutable data). - Borrowing:
view(expr): Creates an immutable borrowtheir<T const>.borrow(expr): Creates a mutable borrowtheir<T>.soft(expr): Creates a mild referencemild<T>fromour<T>.
freedomBlocks: Sections of code markedfreedom { ... }where raw pointers (loc<T>) can be used and some compiler guarantees are relaxed. Within these blocks, operations likeat(ptr)for dereferencing andfrom<loc<T>>()for pointer conversion are available.
Vyn introduces a distinctive approach to module imports with two keywords that serve different security and trust models:
import - Trusted, Verified Modules:
- Used for modules from signed repositories or project-local sources verified in
vyn.toml - Enforces security checks and version verification
- Ideal for production dependencies and standard library modules
- Example:
import std::collections::Vec
smuggle - Flexible, External Sources:
- Allows including symbols from external sources (e.g., GitHub repositories) or unsigned modules
- Bypasses some security checks for rapid prototyping and third-party integration
- Perfect for experimental dependencies, development tools, or one-off utilities
- Example:
smuggle debug::Logger from "https://github.com/user/debug-tools"
This dual system provides both safety for production code and flexibility for development:
# Production imports - verified and trusted
import std::io::println
import utils::math::calculate
# Development/experimental - flexible but marked as such
smuggle debug::trace from "github.com/dev/tools"
smuggle experimental::feature from "./local/experiments"
main()<Int> -> {
println("Production ready!")
trace("Debug info from smuggled module")
return calculate(42)
}
Declare dependencies in vyn.toml:
[dependencies]
std = "^1.0.0" # Signed, from Vyn registry
utils = { git = "https://github.com/user/utils" } # External, smuggledThis unique import/smuggle distinction makes Vyn's module system both secure and flexible, clearly marking the trust level of your dependencies.
Vyn v0.4.4 (freedom-1.0 series) is a complete systems programming language with full native executable generation ready for production use:
- Full Compilation Pipeline: Complete source → object → executable workflow
- Build Command:
vyn program.vyn --build myappcreates standalone executables - Runtime Library: Automatic compilation and linking of Vyn runtime (vyn_runtime.c)
- System Linker Integration: Platform-aware linker selection (lld, ld for Linux; ld64 for macOS)
- C Runtime Initialization: Automatic discovery and linking of CRT files (crt1.o, crti.o, crtn.o)
- Dynamic Linking: Links against system libc and libm by default
- Static Linking: Optional
--staticflag for fully standalone binaries - Production Ready: Deploy native executables without any runtime dependencies
- Cross-Platform: Linux and macOS support with proper platform detection
- Runtime Type Metadata: Complete type registration system with field introspection
- Automatic Serialization:
.to_string()method on structs generates JSON - JSON Deserialization:
T::from_string(json)creates instances from JSON strings - Type Safety: Runtime validation of JSON structure against type definitions
- Supported Types: Int, Float, Bool, String primitives fully working
- Round-Trip Conversion: Full bidirectional struct ↔ JSON conversion
- Zero Boilerplate: No manual serialization code needed
- Test Suite: Comprehensive tests in
test/json/directory
- Object File Emission: Compile to .o files with
--compile/-cflag - Optimization Levels: -O0 (none), -O1 (basic), -O2 (default), -O3 (aggressive)
- Cross-Compilation: Supports 20+ target architectures out of the box
- Debug Information: Full DWARF metadata for debugging compiled code
- Standard Toolchain: Compatible with system linkers (ld, lld, gold)
- ELF Format: Standard relocatable object files for linking
- Functions:
name(params)<ReturnType> -> bodywith full LLVM compilation- Standard parameters:
param<Type>orparam<Type const> - Shorthand parameters:
Type paramorconst Type param - Mixed syntax: Both forms can be used in the same function
- Standard parameters:
- Variables:
name<Type> = valuewith type inference and explicit typing - Resizable Arrays:
Vec<T>withnew(),push(),pop(),len(),get()methods - Vec Iteration:
for (item in vec)loops with full break/continue support - Fixed Arrays:
[T; N]with indexing and beautiful println() output - Structs:
struct Point { x<Int>, y<Int> }with field access (p.x,p.y) - Control Flow:
if/else,while/forloops,matchstatements,break/continue - Select Expressions:
select(expr) -> { pattern -> result };with auto-return and explicitpasskeyword - Arithmetic: Full binary operators (
+,-,*,/,==,!=,<,>, etc.) - Pattern Matching:
match (expr) { pattern -> result }with comprehensive patterns - Error Handling:
fail/trapsystem with zero-cost success path and heap-allocated error contexts for type-safe exception handling - I/O:
println()for output, works with all data types including vectors
- Multi-value returns:
main()<Int,String> -> return 42, "hello" - Variadic Tuples:
Tuple<T,U,V,...>supports 1 to N type parameters with both inline(T,U,V)and genericTuple<T,U,V>syntax - Auto-serialization: Complex return types automatically output as JSON
- Type safety: Full type checking and inference with modern struct syntax
- Generic collections:
Vec<Int>,Vec<String>with full method support - Member access: Struct field access (
obj.field) and array indexing (arr[index])
Vyn v0.4.1 features complete async/await support for writing concurrent programs:
// Async function returning Future<Int>
async compute_value()<Future<Int>> -> {
println("Computing...")
return 42
}
// Async function with await
async process_data()<Future<String>> -> {
value<Int> = await compute_value() // Suspend until future resolves
println("Got value")
return "processed"
}
// Async void function
async background_task()<Future<Void>> -> {
println("Task running...")
return
}
- async keyword: Declares asynchronous functions that return Future
- await expressions: Suspend execution until a Future resolves
- Future types: Type-safe asynchronous return values
- State machines: Async functions compiled to efficient state machines
- Debug support: Full DWARF metadata for debugging async execution
- Suspension tracking: Debug info for continuation points and state transitions
main()<Void> -> {
// Create async tasks
future1<Future<Int>> = compute_value()
future2<Future<String>> = process_data()
// Futures execute concurrently
println("Tasks initiated")
return
}
See: test/async/async_simple.vyn and test/async/async_comprehensive.vyn for working examples
Implementation Status:
- ✅ Async function parsing and validation
- ✅ Future type checking
- ✅ await expression support
- ✅ State machine code generation
- ✅ LLVM codegen with debug metadata
- ✅ Comprehensive test coverage
Vyn features runtime type introspection for self-aware programs:
# Get runtime type hash
var x<Int> = 42
type_id<i64> = typeof(x) # Returns hash of "Int"
# Get type name as string
type_name<String> = typename(x) # Returns "Int"
println(type_name) # Prints: Int
# Works on any expression
result<i64> = typeof(10 + 20) # Type of expression result
func_type<String> = typename(compute()) # Type of function return
- typeof(expr): Returns i64 hash of type name for runtime type comparison
- typename(expr): Returns String with human-readable type name
- Expression-based: Works on variables, literals, function calls, any expression
- LLVM Integration: Uses operand->type->toString() for actual type extraction
- Zero-cost: Type information extracted at compile-time, hashes computed via std::hash
# Type comparison
if (typeof(x) == typeof(y)) {
println("Same types")
}
# Debug output
println("Processing " + typename(data) + " value")
# Foundation for advanced features (planned):
# - Multi-type trap handlers: } trap (e<?>) -> { ... }
# - Generic serialization based on runtime type
# - Type-safe downcasting: value as TargetType
Implementation Status:
- ✅ KEYWORD_TYPEOF and KEYWORD_TYPENAME tokens
- ✅ TypeofExpression and TypenameExpression AST nodes
- ✅ Parser support for typeof(expr) and typename(expr) syntax
- ✅ Semantic analysis (typeof returns "Type", typename returns "string")
- ✅ LLVM codegen with std::hash for typeof, string struct for typename
- ✅ Comprehensive test coverage (4+ test files)
- ✅ println() fix to properly handle Vyn string output
See: test/introspection/ for working examples and doc/INTROSPECTION_DESIGN.md for design philosophy
Philosophy: Vyn uses aspects + structs instead of classes and inheritance. This provides polymorphism, code reuse, and composition without the complexity and pitfalls of OOP class hierarchies. See doc/ASPECT_SYSTEM_DESIGN.md for detailed design and rationale.
# Define an aspect (interface) with method signatures
aspect Display {
show(self<Self>)<String> -> { }
format(self<Self>, prefix<String>)<String> -> { }
}
# Define a generic struct
struct Box<T> {
value<T>
}
# Bind the aspect to a concrete type
bind Display -> Box<Int> {
show(self<Self>)<String> -> {
return "Box with integer"
}
format(self<Self>, prefix<String>)<String> -> {
return prefix + ": Box[Int]"
}
}
# Bind the aspect to another type
struct Point {
x<Int>,
y<Int>
}
bind Display -> Point {
show(self<Self>)<String> -> {
return "Point"
}
format(self<Self>, prefix<String>)<String> -> {
return prefix + ": (x,y)"
}
}
# Generic function using aspect bounds
printItem<T<Display>>(item<T>)<Void> -> {
description<String> = item.show()
formatted<String> = item.format("Item")
println(description)
println(formatted)
}
main()<Int> -> {
box<Box<Int>> = Box<Int> { value = 42 }
point<Point> = Point { x = 10, y = 20 }
# Aspect methods work through generic functions
printItem(box) # Prints: "Box with integer" and "Item: Box[Int]"
printItem(point) # Prints: "Point" and "Item: (x,y)"
return 0
}
✅ Fully Working:
- Aspect Declarations: Define interfaces with method signatures and
Selftype - Bind Blocks: Implement aspects for types using
bind Aspect -> Typesyntax - Generic Aspects: Aspects support generic type parameters
- Generic Bindings:
bind<T> Display -> Box<T>with type parameter extraction - Generic Functions:
printItem<T<Display>>(item<T>)with full monomorphization - Method Calls: Aspect methods called through generic function parameters
- Aspect Registry: Semantic analyzer validates and stores aspect definitions
- Bind Validation: Full signature checking against aspect requirements
- Canonical Syntax: Arrow syntax
->for bindings
Why Aspects > Classes:
- ✅ Multiple aspect implementations (no diamond problem)
- ✅ Composition over inheritance (more flexible)
- ✅ Extension without modification (bind aspects to any type)
- ✅ Static dispatch (zero-cost abstractions)
- ✅ No fragile base class problem
- ✅ Explicit binding makes relationships clear
See: doc/ASPECT_SYSTEM_DESIGN.md for complete specification and test/aspect/ for working examples
Signed Integers:
| Type | Description | Size | Range/Notes | Example |
|---|---|---|---|---|
Int / Int64 |
Signed integer (default) | 64-bit | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | x<Int> = 42 |
Int32 |
32-bit signed integer | 32-bit | -2,147,483,648 to 2,147,483,647 | count<Int32> = 100 |
Int16 |
16-bit signed integer | 16-bit | -32,768 to 32,767 | small<Int16> = 1000 |
Int8 |
8-bit signed integer | 8-bit | -128 to 127 | byte<Int8> = 127 |
Unsigned Integers:
| Type | Description | Size | Range/Notes | Example |
|---|---|---|---|---|
UInt64 |
64-bit unsigned integer | 64-bit | 0 to 18,446,744,073,709,551,615 | large<UInt64> = 1000000 |
UInt32 |
32-bit unsigned integer | 32-bit | 0 to 4,294,967,295 | count<UInt32> = 100 |
UInt16 |
16-bit unsigned integer | 16-bit | 0 to 65,535 | port<UInt16> = 8080 |
UInt8 |
8-bit unsigned integer | 8-bit | 0 to 255 | byte<UInt8> = 255 |
Floating Point:
| Type | Description | Size | Precision | Example |
|---|---|---|---|---|
Float / Float64 |
Double precision (default) | 64-bit | IEEE 754 double (~15-17 digits) | pi<Float> = 3.14159 |
Float32 |
Single precision | 32-bit | IEEE 754 single (~6-9 digits) | ratio<Float32> = 1.5 |
Character Types:
| Type | Description | Size | Range/Notes | Example |
|---|---|---|---|---|
Char |
UTF-8 code unit | 8-bit | Single byte (0-255) | ch<Char> = 65 # 'A' |
Rune |
Unicode code point | 32-bit | Full Unicode range (U+0000 to U+10FFFF) | emoji<Rune> = 128512 # 😀 |
Other Types:
| Type | Description | Size | Range/Notes | Example |
|---|---|---|---|---|
Bool |
Boolean | 1-bit | true or false |
flag<Bool> = true |
String |
UTF-8 string | Variable | Heap-allocated fat pointer { ptr, len } |
name<String> = "Alice" |
Bytes |
Raw binary data | Variable | Fat pointer for byte sequences { ptr, len } |
Future byte literals |
Void |
No value | 0-bit | Used for functions that don't return | print()<Void> -> { ... } |
| Type | Description | Mutability | Example |
|---|---|---|---|
[T; N] |
Fixed-size array | Mutable elements | nums<[Int; 5]> = [1, 2, 3, 4, 5] |
Vec<T> |
Dynamic array | Mutable elements | items<Vec<String>> = Vec::new() |
Tuple<T,U,...> |
Heterogeneous tuple (variadic) | Immutable | data<Tuple<Int,String,Bool>> |
| Type | Description | Use Case | Example |
|---|---|---|---|
my<T> |
Unique ownership | Single owner, move semantics | data<my<Person>> = my(Person{...}) |
our<T> |
Shared ownership | Reference counting | config<our<Settings>> = our(Settings{...}) |
their<T> |
Borrowed reference | Non-owning access | ref<their<Data>> = view(owner) |
loc<T> |
Raw pointer | Freedom operations only | ptr<loc<Int>> = loc(variable) |
Vyn v0.4.2 features unified canonical syntax for ownership and borrowing operations:
# In variable declarations and function signatures
data<my<String>> # Unique ownership type
shared<our<Config>> # Shared ownership type
view<their<Data>> # Borrowed reference type
# Create owned values with my() and our() constructors
unique<my<String>> = my("owned string")
shared<our<Config>> = our(Config::new())
result<my<Data>> = my(compute_data())
# Create temporary references with view/borrow/soft functions
readonly<their<String const>> = view(data) # Immutable borrow
writable<their<String>> = borrow(data) # Mutable borrow
weak<mild<Node>> = soft(shared) # Weak reference
length<Int> = view(data).len()
borrow(data).clear()
All legacy make_my(), make_our() functions and view(), borrow() function calls have been automatically migrated to canonical syntax using the migration tool:
# Scan for legacy syntax
python3 migrate_syntax.py --scan --directory .
# Apply migrations with backup
python3 migrate_syntax.py --migrate --directory . --backup --reportMigration Results: ✅ 346 syntax updates applied across 22 files, ensuring consistent canonical syntax throughout the entire codebase.
- Ownership types:
my<T>,our<T>,their<T>,mild<T>for safe memory handling - Mild references:
mild<T>created withsoft()for breaking circular references withgrab()andreleased()methods - Borrowing:
view(expr)andborrow(expr)for references - Freedom operations:
loc<T>pointers infreedom {}blocks - Raw Memory Operations: Complete
freedomblock system withloc<T>pointers - Memory Safety: Borrow checking and lifetime analysis prevent dangling pointers
Vyn v0.4.4 introduces mild<T> - weak references that solve circular reference problems without preventing cleanup:
Why mild?
- Break Cycles: Tree nodes with parent pointers, doubly-linked lists
- Observer Pattern: Subjects don't keep observers alive
- Caches: Entries that can be collected when memory is needed
- Safe Access: Can detect when target object has been destroyed
Key Methods:
# grab() -> our<T>?
# Attempts to upgrade mild reference to strong reference
# Returns nil if target has been destroyed
parent_strong<our<TreeNode>> = node.parent.grab()
# released() -> Bool
# Check if target object has been freed
# Returns true if destroyed, false if still alive
is_alive<Bool> = !node.parent.released()
Creating Mild References:
# Use soft() to create mild<T> from our<T>
shared_data<our<Config>> = our(Config::new())
shadow<mild<Config>> = soft(shared_data) # Create mild reference
Example: Tree with Parent Pointers
struct TreeNode {
value<Int>,
children<Vec<our<TreeNode>>>, # Strong refs to children
parent<mild<TreeNode>> # Mild ref to parent (breaks cycle)
}
create_child(parent<our<TreeNode>>, value<Int>)<our<TreeNode>> -> {
return our(TreeNode {
value: value,
children: Vec::new(),
parent: soft(parent) # Create weak reference with soft()
})
}
access_parent(node<our<TreeNode>>)<Int> -> {
# Safely access parent through weak reference
if (parent<our<TreeNode>> = node.parent.grab()) {
return parent.value # Success - parent still alive
} else {
return -1 # Parent was destroyed
}
}
See: doc/OWNERSHIP_MILD.md for complete documentation and test/ownership/mild_test.vyn for examples
- LLVM backend: Direct compilation to native code with JIT execution
- Comprehensive parser: Handles complex syntax including templates, async, traits
- Rich error messages: Clear compilation feedback
- Advanced Test Harness: Modern parallel test runner with HTML/JSON reporting and triage analysis
- Debug Information: Complete DWARF debug metadata generation for debugging async state machines
- Git integration: Regular commits track development progress
Vyn v0.4.2 (freedom-1.0 series) is a complete, production-ready systems programming language with modern syntax, complete sized type system, powerful pattern matching, generic functions, and comprehensive collection support.
# Complete language demonstration showing all major features
# Modern struct syntax with typed fields
struct Person {
name<String>,
age<Int>,
scores<Vec<Int>>
}
# Define aspect for gradeable entities
aspect Gradeable {
letter_grade(self<Self>)<String> -> { }
is_passing(self<Self>)<Bool> -> { }
}
# Bind aspect to Person using match in the implementation
bind Gradeable -> Person {
letter_grade(self<Self>)<String> -> {
# Calculate average score
total<Int> = 0
i<Int> = 0
while (i < self.scores.len()) {
total = total + self.scores.get(i)
i = i + 1
}
avg<Int> = total / self.scores.len()
# Match statement with comparison patterns for grade ranges
match (avg) {
>= 90 -> return "A",
>= 80 -> return "B",
>= 70 -> return "C",
>= 60 -> return "D",
? -> return "F"
}
}
is_passing(self<Self>)<Bool> -> {
grade<String> = self.letter_grade()
match (grade) {
"F" -> return false,
? -> return true
}
}
}
# Select expression with comparison patterns returning grade string
get_grade_description(score<Int>)<String> -> {
grade<String> = select(score) -> {
>= 90 -> { pass "Excellent" },
>= 80 -> { pass "Good" },
>= 70 -> { pass "Satisfactory" },
>= 60 -> { pass "Passing" },
? -> { pass "Needs Improvement" }
};
println(grade)
return grade
}
# Generic function using aspect bounds
print_student_status<T<Gradeable>>(student<T>)<Void> -> {
grade<String> = student.letter_grade()
passing<Bool> = student.is_passing()
println("Grade: " + grade)
if (passing) {
println("Status: PASSING")
} else {
println("Status: FAILING")
}
}
# Resizable collections with full method support
create_person(name<String>, age<Int>)<Person> -> {
person<Person> = Person {
name = name,
age = age,
scores = Vec::new()
}
# Member access for both reading and modification
person.scores.push(85)
person.scores.push(92)
person.scores.push(78)
return person
}
main()<Int> -> {
student<Person> = create_person("Alice", 20)
# Call aspect method through generic function
print_student_status(student)
# Use select expression with comparison patterns
description<String> = get_grade_description(85)
println(student) # Auto-serialization of complex types
return 0
}
Vyn's design philosophy: FREEDOM over restrictions. The language provides compiler-managed ownership by default, but empowers programmers with low-level control when needed:
# Restricted memory management with ownership types
restricted_memory_example()<Int> -> {
# Unique ownership - automatically freed when out of scope
owned<my<String>> = my("unique data")
# Shared ownership - reference counted
shared<our<String>> = our("shared data")
another_ref<our<String>> = shared # Reference count incremented
# Borrowing - non-owning references
view_ref<their<String const>> = view(shared) # Immutable borrow
mut_ref<their<String>> = borrow(owned) # Mutable borrow
return 42
}
# FREEDOM operations for low-level control
freedom_memory_example()<Int> -> {
x<Int> = 42
result<Int> = 0
freedom {
# Create raw pointers
p<loc<Int>> = loc(x) # Get pointer to x
q<loc<Int>> = loc(result) # Get pointer to result
# Read and write through pointers
at(q) = at(p) * 2 # Write x*2 to result through pointers
# Pointer type conversion
p_void<loc<Void>> = from<loc<Void>>(p)
p_back<loc<Int>> = from<loc<Int>>(p_void)
}
return result # Returns 84
}
FREEDOM Code Guidelines:
- Minimize the scope of
freedomblocks - Document all invariants and assumptions
- Validate pointers before dereferencing
- Encapsulate freedom operations behind restricted abstractions
- Test freedom code extensively
Vyn uses indentation-sensitive syntax with optional braces and semicolons. Whitespace defines blocks, so consistent indentation is key. The unified name<Type> syntax provides consistency across all language constructs.
# Integer literals
x<Int> = 42 # 64-bit signed integer (default)
small<Int> = 123 # All integers default to 64-bit
large<Int> = -9223372036854775808 # Full 64-bit range
# Floating point literals
pi<Float> = 3.14159 # 64-bit IEEE 754 double precision
scientific<Float> = 1.23e-4 # Scientific notation supported
negative<Float> = -2.718 # Negative floats
# Boolean literals
flag<Bool> = true # Boolean true
active<Bool> = false # Boolean false
# String literals
name<String> = "Alice" # UTF-8 string literals
multiline<String> = "Line 1\nLine 2" # Escape sequences supported
empty<String> = "" # Empty strings
# Character and Unicode (planned)
# ch<Char> = 'A' # Single byte UTF-8 code unit (planned)
# rune<Rune> = '💡' # Full Unicode code point (planned)
# Array literals
numbers<[Int; 5]> = [1, 2, 3, 4, 5] # Fixed-size arrays
mixed<[Float; 3]> = [1.0, 2.5, -3.14] # Different element types
empty_array<[Int; 0]> = [] # Empty arrays
# Vector literals (dynamic arrays)
items<Vec<Int>> = Vec::new() # Empty dynamic vector
# Dynamic vector literals with vec![] syntax planned
# Raw bytes (planned)
# raw<Bytes> = [0xDE, 0xAD, 0xBE] # Raw byte sequences (planned)
# Null/void
# No explicit null literal - use Option<T> pattern when implemented
# Variable declarations with unified syntax
x<Int> = 42 # Mutable variable
PI<Float const> = 3.14159 # Immutable constant
# Function declarations follow execution order
add(a<Int>, b<Int>)<Int> -> a + b
# Struct definitions with typed fields
struct Point {
x<Float>,
y<Float> = 0.0 # Default values supported
}
# Pattern matching with comprehensive patterns
process_value(val<Int>)<String> -> {
match (val) {
0 -> "zero",
1 -> "small", # More patterns planned
? -> "large"
}
}
# Memory safety with ownership types
safe_data<my<String>> = my("unique") # Unique ownership
shared_data<our<String>> = our("shared") # Reference counted
borrowed<their<String>> = borrow(safe_data) # Non-owning reference
shadow<mild<String>> = soft(shared_data) # Mild reference (breaks cycles)
Current Primitive Types:
- Signed integers:
Int/Int64(default),Int32,Int16,Int8 - Unsigned integers:
UInt64,UInt32,UInt16,UInt8 - Floating point:
Float/Float64(default),Float32 - Characters:
Char(UTF-8 code unit),Rune(Unicode code point) - Binary data:
Bytes(fat pointer for raw bytes) - Other:
Bool,String,Void
Current Collection Types:
- Fixed arrays:
[T; N]with compile-time size - Dynamic arrays:
Vec<T>resizable collections - Tuples:
Tuple<T,U,...>variadic heterogeneous types
Ownership Types: my<T> (unique), our<T> (shared), their<T> (borrowed), mild<T> (mild reference), loc<T> (freedom raw pointer)
Type Aliasing: All numeric types support multiple naming conventions:
- Vyn style:
Int32,Float64,UInt8 - C style:
int32,float64,uint8 - LLVM style:
i32,f64,u8
Vyn uses clean, expressive syntax with flexible parameter syntax:
# Functions support both standard and shorthand parameter syntax
# Standard syntax: explicit parameter<Type> or parameter<Type const>
add_standard(a<Int>, b<Int const>)<Int> -> {
return a + b
}
# Shorthand syntax: Type directly (implicitly mutable)
add_shorthand(Int a, Int b)<Int> -> {
return a + b
}
# Mixed syntax: combining both forms in same function
mixed_params(x<Int>, Int y, const Int z)<Int> -> {
return x + y + z
}
# Complex return types with auto-serialization
get_data()<Int, String, Bool> -> {
return 42, "result", true
}
# When called from main(), outputs: {"Int":42,"String":"result","Bool":true}
Vyn supports two parameter syntax styles for maximum flexibility:
# Standard syntax: explicit mutability
format_standard(prefix<String>, value<Int const>)<String> -> {
return prefix + String::from_int(value)
}
# Shorthand syntax: type-first (more concise)
format_shorthand(String prefix, const Int value)<String> -> {
return prefix + String::from_int(value)
}
# Both produce identical behavior and LLVM IR
# Variables with type inference
x = 42 # Int
name = "Alice" # String
flag = true # Bool
# Explicit typing
count<Int> = 0
message<String> = "Hello"
# Immutable values (const is a type modifier)
PI<Float const> = 3.14159
MAX_SIZE<Int const> = 1000
# Define a struct with modern syntax
struct Point {
x<Int>,
y<Int>
}
# Create and use with field access
main()<Int> -> {
p<Point> = Point { x = 10, y = 20 }
return p.x + p.y # Returns 30
}
Vyn's unique dual import system provides both security and flexibility:
# Trusted imports from verified sources
import std::collections::Vec
import utils::math::calculate
import std::io::println
# Flexible imports for development and experimentation
smuggle debug::trace from "github.com/dev/tools"
smuggle experimental::parser from "./local/experiments"
# Use imported symbols
main()<Int> -> {
numbers<Vec<Int>> = Vec::new()
numbers.push(calculate(10))
println(numbers) # From trusted std::io
trace("Debug info") # From smuggled debug module
return numbers.get(0)
}
Import Types:
import: For signed, verified modules from registries or project dependenciessmuggle: For external, experimental, or development-only modules
Declare dependencies in vyn.toml:
[dependencies]
std = "^1.0.0" # Verified registry package
utils = { git = "https://..." } # External Git repository
local_tools = { path = "../tools" } # Local development dependencyPlanned Bundle & Sharing System: Fine-grained visibility control with bundles:
# Declare module bundles
bundle(math, math.Core)
# Control symbol visibility
share(all) fn public_function() { ... } # Public to all
share(math.UI) fn ui_helper() { ... } # Only to math.UI bundle
fn internal_helper() { ... } # Private to this file
# Resizable vectors with full method support
main()<Int> -> {
numbers<Vec<Int>> = Vec::new()
numbers.push(10)
numbers.push(20)
numbers.push(30)
println(numbers) # Outputs: { "type": "Vec<Int>", "address": "0x..." }
length<Int> = numbers.len() # Gets 3
first<Int> = numbers.get(0) # Gets 10
last<Int> = numbers.pop() # Gets and removes 30
return first + last # Returns 40
}
# Vec iteration - parentheses mandatory
iterate_example()<Int> -> {
items<Vec<Int>> = Vec::new()
items.push(1)
items.push(2)
items.push(3)
sum<Int> = 0
for (item in items) {
sum = sum + item
}
return sum # Returns 6
}
# Fixed-size arrays also supported
array_example()<Int> -> {
fixed<[Int; 3]> = [1, 2, 3]
return fixed[0] # Array indexing
}
# Conditional expressions with modern syntax
check_sign(x<Int>)<String> -> {
if (x > 0) {
return "positive"
} else if (x < 0) {
return "negative"
} else {
return "zero"
}
}
# While loops with break/continue
factorial(n<Int>)<Int> -> {
result<Int> = 1
i<Int> = 1
while (i <= n) {
if (i == 0) {
continue
}
result = result * i
i = i + 1
if (result > 1000) {
break
}
}
return result
}
# Vec iteration with for loops (parentheses mandatory)
sum_vector(numbers<Vec<Int>>)<Int> -> {
total<Int> = 0
for (num in numbers) {
if (num < 0) {
continue # Skip negative numbers
}
total = total + num
if (total > 100) {
break # Stop if sum exceeds 100
}
}
return total
}
# Range-based for loops (inclusive)
count_to_ten()<Int> -> {
sum<Int> = 0
for (i in 0..10) {
sum = sum + i
}
return sum # Returns 55
}
# Pattern matching with match statements
describe_number(x<Int>)<String> -> {
match (x) {
0 -> return "zero",
1 -> return "one",
42 -> return "the answer",
? -> return "some number"
}
}
# Match arms can execute any statement, including return
# The match statement itself doesn't return a value, but pattern arms can
# return from the enclosing function when the type matches
# Select expressions - elegant pattern matching that returns a value
compute_bonus(level<Int>)<Int> -> {
# Simple select with naked expressions (auto-return)
multiplier<Int> = select(level) -> {
1 -> 10,
2 -> 20,
3 -> 30,
? -> 5
};
return multiplier * 100
}
# Select with complex blocks and explicit pass keyword
process_request(code<Int>)<Int> -> {
result<Int> = select(code) -> {
1 -> {
temp<Int> = 10;
pass temp
},
2 -> {
x<Int> = 20;
pass x
},
3 -> {
msg<Int> = 300;
println(msg);
pass msg
},
? -> {
pass 0
}
};
return result
}
# Select expressions combine the best of match and expression values:
# - Naked expressions (1 -> 10) auto-return without 'pass'
# - Complex blocks ({ ... }) use 'pass' keyword to return value
# - 'pass' returns from the block, NOT the enclosing function
# - Type inference from first case ensures type safety
# - Wildcard '?' provides default case handling
Vyn's pattern matching supports comparison patterns for elegant range-based matching with compile-time safety:
# Comparison operators in patterns: >, <, >=, <=, ==, !=
# Select expression - returns a value directly
classify_score(score<Int>)<String> -> {
return select(score) -> {
>= 90 -> "A",
>= 80 -> "B",
>= 70 -> "C",
>= 60 -> "D",
? -> "F"
}
}
# Match statement - returns from enclosing function
classify_score_match(score<Int>)<String> -> {
match (score) {
>= 90 -> return "A",
>= 80 -> return "B",
>= 70 -> return "C",
>= 60 -> return "D",
? -> return "F"
}
}
# Tax rate calculation with select
compute_tax_rate(income<Int>)<Float> -> {
return select(income) -> {
< 10000 -> 0.0,
< 50000 -> 0.15,
< 100000 -> 0.25,
? -> 0.35
}
}
# Comparison patterns work with floats and integers
categorize_temperature(temp<Float>)<String> -> {
match (temp) {
< 0.0 -> return "freezing",
< 10.0 -> return "cold",
< 20.0 -> return "mild",
< 30.0 -> return "warm",
? -> return "hot"
}
}
# Unreachable pattern detection - compiler ERROR
invalid_patterns(x<Int>)<String> -> {
match (x) {
> 10 -> return "big",
> 5 -> return "medium", # ERROR: unreachable (subsumed by > 10)
? -> return "small"
}
}
# Other unreachable pattern errors:
# - Wildcard before end: match (x) { ? -> "any", 5 -> "five" } # ERROR
# - Duplicate patterns: match (x) { > 10 -> "a", > 10 -> "b" } # ERROR
# - Overlapping ranges: match (x) { > 5 -> "a", >= 3 -> "b" } # ERROR
Comparison Pattern Rules:
- Evaluation Order: Patterns tested top-to-bottom, first-match-wins
- Type Safety: Works with
Int,Floattypes (more types planned) - Compile-Time Errors: Unreachable patterns detected and rejected
- Wildcard Position:
?must be last pattern or compiler ERROR - No Gaps Required:
>= 90,>= 80,>= 70is valid (evaluates top-to-bottom)
Match vs Select with Comparison Patterns:
select: Expression that evaluates to a value - use naked expressions orpasskeywordmatch: Statement for side effects - pattern arms canreturnfrom enclosing function- Both support identical comparison pattern syntax and unreachable pattern detection
### Variadic Tuples
Vyn supports **fully variadic tuple types** that can hold any number of heterogeneous elements (1 to N):
```vyn
# Single-element tuples
get_single()<Tuple<Int>> -> {
return 42 # Automatically wrapped in tuple struct
}
# Two-element tuples (dual syntax)
get_pair_inline()<Int, String> -> {
return 10, "hello" # Inline syntax
}
get_pair_generic()<Tuple<Int, String>> -> {
return 20, "world" # Generic syntax (equivalent)
}
# Multi-element tuples with mixed types
get_data()<Tuple<String, Int, Bool, Float>> -> {
return "status", 200, true, 3.14
}
# Seven-element tuple demonstrating variadic support
get_complex()<Tuple<Int, Int, Bool, String, Int, Bool, Int>> -> {
return 1, 2, true, "test", 3, false, 4
}
# Tuples work seamlessly with complex types
get_mixed()<Tuple<String, Vec<Int>, Bool>> -> {
numbers<Vec<Int>> = Vec::new()
numbers.push(1)
numbers.push(2)
return "data", numbers, true
}
main()<Int> -> {
# All tuple syntaxes work identically
single<Tuple<Int>> = get_single()
pair<Tuple<Int,String>> = get_pair_inline()
data<Tuple<String,Int,Bool,Float>> = get_data()
return 0
}
Tuple Features:
- Variadic: Supports 1 to N type parameters (tested up to 7+ elements)
- Dual Syntax: Both
(T,U,V)inline andTuple<T,U,V>generic forms - Type Safety: Full compile-time type checking for all elements
- Complex Types: Works with primitives, Strings, Vec, and custom types
- Auto-wrapping: Single values automatically wrapped when returning to tuple type
- LLVM Structs: Compiled to efficient anonymous struct types
Current Limitations (planned for future releases):
- No tuple element access (
.0,.1, etc.) - No tuple variables (only return values)
- No tuple destructuring
- No tuple serialization output (compiles but values not printed)
Vyn's String type is a production-ready fat pointer implementation with comprehensive method support and natural literal syntax. Unlike C's null-terminated strings or C++'s heavyweight std::string, Vyn Strings combine the best of both worlds: efficient representation with modern conveniences.
# Internally, String is a fat pointer struct:
struct String {
ptr: *i8, # Pointer to null-terminated byte data
len: i64 # Length (excluding null terminator)
}
This design provides:
- O(1) length queries - No strlen() scanning needed
- C interoperability - Null termination for printf, strstr, etc.
- Memory efficiency - Just 16 bytes overhead per string
- Safe indexing - Built-in bounds checking
String literals in Vyn are first-class citizens:
# Direct literal assignment
greeting<String> = "Hello, Vyn!"
# Literal concatenation (just works™)
message<String> = "Hello" + " " + "World"
# Method calls on literals
length<Int> = "Vyn".len() # Returns 3
first<Int> = "Quantum".char_at(0) # Returns 'Q' (81)
check<Bool> = "Einstein".starts_with("Ein") # Returns true
# All without explicit constructors!
Constructor
# Create from raw bytes (C interop)
name<String> = String::from_bytes("Alice", 5)
raw<String> = String::from_bytes(c_ptr, c_len)
Property Access
msg<String> = "Hello"
length<Int> = msg.len() # Returns 5, O(1) operation
Substring Operations
text<String> = "Hello World"
# Extract substring (end optional)
hello<String> = text.substring(0, 5) # "Hello"
world<String> = text.substring(6, 11) # "World"
rest<String> = text.substring(6) # "World" (to end)
# Safe character access with bounds checking
first<Int> = text.char_at(0) # 72 ('H')
space<Int> = text.char_at(5) # 32 (' ')
invalid<Int> = text.char_at(99) # 0 (null char for out of bounds)
Search and Comparison
sentence<String> = "The quick brown fox"
# Prefix/suffix checking
has_the<Bool> = sentence.starts_with("The") # true
has_fox<Bool> = sentence.ends_with("fox") # true
has_dog<Bool> = sentence.ends_with("dog") # false
# Substring search
has_quick<Bool> = sentence.contains("quick") # true
has_slow<Bool> = sentence.contains("slow") # false
# Empty strings always match
always<Bool> = sentence.starts_with("") # true
Case Conversion
mixed<String> = "Hello World"
# ASCII case conversion (allocates new string)
upper<String> = mixed.to_upper() # "HELLO WORLD"
lower<String> = mixed.to_lower() # "hello world"
# Original unchanged (immutability)
println(mixed) # Still "Hello World"
String Concatenation
# Using + operator (most natural)
full<String> = "Hello" + " " + "World"
# Chaining operations
result<String> = "Vyn".to_upper() + " " + "Language".to_lower()
# Result: "VYN language"
# Mixed types (planned with toString())
# message<String> = "Count: " + 42.to_string()
Text Processing
process_input(text<String>)<Bool> -> {
# Validate input
if (text.len() == 0) {
return false
}
# Check for command prefix
if (text.starts_with("/")) {
command<String> = text.substring(1)
println("Command: " + command)
return true
}
# Search for keywords
if (text.contains("help")) {
println("Help requested")
return true
}
return false
}
String Manipulation
format_name(first<String>, last<String>)<String> -> {
# Capitalize first letter of each name
first_upper<String> = first.char_at(0).to_string().to_upper() +
first.substring(1).to_lower()
last_upper<String> = last.char_at(0).to_string().to_upper() +
last.substring(1).to_lower()
# Combine with space
return first_upper + " " + last_upper
}
main()<Int> -> {
formatted<String> = format_name("ALICE", "wonderland")
println(formatted) # "Alice Wonderland"
return 0
}
Data Validation
validate_email(email<String>)<Bool> -> {
# Simple email validation
if (email.len() < 3) {
return false # Too short
}
if (!email.contains("@")) {
return false # No @ symbol
}
# Check @ is not first or last
at_pos<Int> = email.find("@") # (planned method)
if (at_pos == 0 || at_pos == email.len() - 1) {
return false
}
return true
}
Allocation Strategy
# Read-only operations: ZERO allocations
len<Int> = "Hello".len() # No malloc
ch<Int> = "World".char_at(0) # No malloc
check<Bool> = "Test".starts_with("Te") # No malloc
# Transform operations: Allocate new strings
upper<String> = "hello".to_upper() # malloc(6) for "HELLO\0"
sub<String> = "Hello World".substring(0, 5) # malloc(6) for "Hello\0"
concat<String> = "A" + "B" # malloc(3) for "AB\0"
# Ownership handles cleanup automatically
# (when ownership system is fully integrated)
Bounds Safety
# All index operations are bounds-checked at runtime
text<String> = "Vyn"
safe<Int> = text.char_at(2) # OK: returns 'n' (110)
safe2<Int> = text.char_at(0) # OK: returns 'V' (86)
# Out of bounds returns safe defaults
oob1<Int> = text.char_at(-1) # Returns 0 (null char)
oob2<Int> = text.char_at(100) # Returns 0 (null char)
# substring returns empty string for invalid bounds
empty<String> = text.substring(10, 20) # Returns {null, 0}
| Operation | Time | Space | Notes |
|---|---|---|---|
len() |
O(1) | O(1) | Just reads struct field |
char_at(i) |
O(1) | O(1) | Bounds-checked array access |
starts_with(s) |
O(k) | O(1) | k = prefix length, uses memcmp |
ends_with(s) |
O(k) | O(1) | k = suffix length, uses memcmp |
contains(s) |
O(n×k) | O(1) | Uses C strstr (KMP-like) |
substring(i,j) |
O(k) | O(k) | k = j-i, allocates new buffer |
to_upper() |
O(n) | O(n) | Allocates new buffer, ASCII only |
to_lower() |
O(n) | O(n) | Allocates new buffer, ASCII only |
a + b |
O(n+m) | O(n+m) | Allocates new buffer |
All String methods produce null-terminated strings for C compatibility:
# Use with C functions (planned FFI)
name<String> = "Alice"
c_str<*i8> = name.to_bytes() # Get raw pointer
# Compatible with:
# printf("%s", c_str)
# strlen(c_str)
# strcmp(c_str1, c_str2)
# strstr(haystack, needle)
Planned Methods
split(delimiter: String) -> Vec<String>- Split into vector of substringstrim() -> String- Remove leading/trailing whitespacereplace(old: String, new: String) -> String- Replace all occurrencesfind(substring: String) -> Int- Get index of first occurrenceparse_int() -> Option<Int>- Parse string to integerformat(args...)- String interpolation
Advanced Features
- UTF-8 support: Unicode-aware operations (length, indexing, case)
- Small string optimization: Inline strings ≤15 bytes (no heap allocation)
- String interning: Share identical string data
- Rope data structure: Efficient concatenation for large strings
- Copy-on-write: Share data until modification
# Simple things are simple
msg<String> = "Hello" + " " + "World"
# Complex things are possible (when needed)
advanced<String> = data
.to_lower()
.substring(0, 100)
.replace("old", "new") # (planned)
.trim() # (planned)
String Theory Achievement Unlocked: You now understand Vyn Strings better than most physicists understand actual string theory! 🎻✨
Vyn v0.4.2 features an explicit error handling system that combines the clarity of exceptions with the safety of Result types. The trap/fail/ensure trio provides compile-time error tracking with zero runtime overhead for the happy path.
Traditional error handling offers two flawed extremes:
- Exceptions: Hidden control flow, unclear what can fail, runtime overhead
- Result Types: Verbose unwrapping, easy to ignore errors, cluttered code
Vyn's approach:
- Explicit propagation: Errors visible in type signatures
- Zero-cost success: No overhead when operations succeed
- Pattern matching: Handle errors elegantly with full type safety
- Heap allocation: Errors carry rich context through pointer passing
Functions declare error returns in their signatures using failable types:
# Functions that can fail return (T, error_ptr) tuples
divide(a<Int>, b<Int>)<Int> -> {
if (b == 0) {
fail 42 # Return error with code 42
}
return a / b
}
# The compiler wraps this as: { i64 result, i8* error } in LLVM
Type System Integration:
- Success path: Returns
(value, null_ptr)tuple - Error path: Returns
(undefined, error_ptr)tuple - Error pointers carry heap-allocated error structs
- Type IDs enable pattern matching on error types
Create and propagate errors with fail:
# Simple error codes (primitive types)
fail 404 # Integer error
fail "not found" # String error
fail 3.14 # Float error
# Structured errors (custom types)
struct DivisionError {
code<Int>,
dividend<Int>,
divisor<Int>
}
divide_structured(a<Int>, b<Int>)<Int> -> {
if (b == 0) {
fail DivisionError {
code = 42,
dividend = a,
divisor = b
}
}
return a / b
}
Error Memory Layout:
Heap-allocated error struct (16 bytes):
Offset 0-7: Type ID hash (i64) - For pattern matching
Offset 8-15: Error value/data - Primitive or struct data
Type ID Hashing:
Int→ hash("Int") = -3994496327427856726String→ hash("String") = unique valueDivisionError→ hash("DivisionError") = unique value- Enables runtime type dispatch in trap handlers
Handle errors with pattern matching:
# Basic trap with single error type
compute_safely(x<Int>, y<Int>)<Int> -> {
result<Int> = {
value<Int> = divide(x, y) # May fail
value * 2
} trap (e<Int>) -> {
println("Division failed with code: " + String::from_int(e))
-1 # Return fallback value
}
return result
}
# Multiple error types with pattern matching
process_data(x<Int>)<String> -> {
{
value<Int> = risky_operation(x)
"Success: " + String::from_int(value)
} trap (e<Int>) -> {
"Integer error: " + String::from_int(e)
} trap (e<String>) -> {
"String error: " + e
} trap (e<DivisionError>) -> {
"Division error code: " + String::from_int(e.code)
}
}
# Wildcard trap - catch any error type
safe_operation(x<Int>)<Int> -> {
result<Int> = {
risky_call(x)
} trap (e<DivisionError>) -> {
println("Known error: division by zero")
return 0
} trap (e<?>) -> {
println("Unknown error caught by wildcard!")
return -1
}
return result
}
# Multi-type trap - catch union of error types
process_data(input<String>)<Int> -> {
result<Int> = {
parse_and_validate(input)
} trap (e<ParseError | ValidationError>) -> {
# Handler matches either ParseError or ValidationError
println("Input processing failed")
return -1
} trap (e<NetworkError | TimeoutError>) -> {
# Different handler for network-related errors
println("Network issue, retrying...")
return retry_operation(input)
}
return result
}
Pattern Matching Features:
- Type-safe: Only valid error types accepted
- Exhaustive: Compiler ensures all error types handled
- Field access: Access struct fields in trap handlers (
e.code,e.dividend) - Wildcard: Use
} trap (e<?>) -> { }to catch any error type - Multi-type: Use
} trap (e<Type1 | Type2>) -> { }to catch union of types
Errors automatically propagate through call stacks:
# Three-level error propagation
divide(a<Int>, b<Int>)<Int> -> {
if (b == 0) {
fail 42 # Create error at bottom
}
return a / b
}
compute(x<Int>, y<Int>)<Int> -> {
result<Int> = divide(x, y) # Propagates error if divide fails
return result + 10 # Only reached if divide succeeds
}
main()<Int> -> {
val1<Int> = compute(10, 2) # OK: 10/2 + 10 = 15
val2<Int> = {
compute(10, 0) # Fails: division by zero
} trap (e<Int>) -> {
println("Caught error: " + String::from_int(e))
-1 # Return fallback
}
return val1 + val2 # Returns 15 + (-1) = 14
}
Propagation Mechanics:
divide(10, 0)allocates error on heap, returns(undef, error_ptr)computechecks error pointer, finds non-null, propagates(undef, error_ptr)maintrap block catches error, extracts type and value- Error memory freed after handling
Errors that escape without trap handlers trigger runtime termination:
divide(a<Int>, b<Int>)<Int> -> {
if (b == 0) {
fail 42
}
return a / b
}
main()<Int> -> {
result<Int> = divide(10, 0) # No trap handler!
return result
}
Runtime Output:
┌─ UNTRAPPED FAILURE ──────────────────────────────────────────┐
│ Error: <runtime error> │
│ Thread: 6472648897627130861 │
│ Time: 2025-10-21 10:01:29.810 │
└──────────────────────────────────────────────────────────────┘
Exit Code: 1
Safety Guarantees:
- No silent failures
- No undefined behavior
- Clear error location (planned: stack traces)
- Graceful termination
Rich error types carry debugging context:
struct ValidationError {
field_name<String>,
expected<String>,
actual<String>,
line_number<Int>
}
validate_input(input<String>)<Bool> -> {
if (input.len() == 0) {
fail ValidationError {
field_name = "input",
expected = "non-empty string",
actual = "empty string",
line_number = 42
}
}
return true
}
main()<Int> -> {
{
is_valid<Bool> = validate_input("")
0
} trap (e<ValidationError>) -> {
println("Validation failed:")
println(" Field: " + e.field_name)
println(" Expected: " + e.expected)
println(" Actual: " + e.actual)
println(" Line: " + String::from_int(e.line_number))
1
}
}
Output:
Validation failed:
Field: input
Expected: non-empty string
Actual: empty string
Line: 42
The ensure keyword provides cleanup/finally semantics, guaranteeing code runs whether the block succeeds or fails:
# Implemented in v0.4.2
process_file(path<String>)<String> -> {
file<File> = open_file(path)
result<String> = {
file.read()
} trap (e<IOError>) -> {
return ""
} ensure -> {
file.close() # Always runs, success or failure
}
return result
}
# Execution order on error:
# 1. Block code executes and fails
# 2. Matching trap handler runs (if present)
# 3. ensure block always runs last
# Implementation details:
# - Parser fully supports } ensure -> { } syntax
# - Codegen inlines ensure blocks into control flow
# - Works with both success and failure paths
# - See: test/trap/test_ensure_simple.vyn
The error handling system compiles to efficient LLVM IR:
; Failable function returning {i64 result, i8* error}
define { i64, ptr } @divide(i64 %a, i64 %b) {
entry:
%is_zero = icmp eq i64 %b, 0
br i1 %is_zero, label %error_path, label %success_path
error_path:
; Allocate error struct: 8 bytes type_id + 8 bytes value
%error_mem = call ptr @malloc(i64 16)
; Store type ID at offset 0
%type_id = i64 -3994496327427856726 ; hash("Int")
store i64 %type_id, ptr %error_mem
; Store error value at offset 8
%value_ptr = getelementptr i8, ptr %error_mem, i64 8
store i64 42, ptr %value_ptr
; Return (undef, error_ptr) tuple
%result = insertvalue { i64, ptr } undef, ptr %error_mem, 1
ret { i64, ptr } %result
success_path:
%quotient = sdiv i64 %a, %b
; Return (quotient, null) tuple
%success = insertvalue { i64, ptr } undef, i64 %quotient, 0
%success2 = insertvalue { i64, ptr } %success, ptr null, 1
ret { i64, ptr } %success2
}
; Call site with error checking
define { i64, ptr } @compute(i64 %x, i64 %y) {
entry:
%call = call { i64, ptr } @divide(i64 %x, i64 %y)
%value = extractvalue { i64, ptr } %call, 0
%error = extractvalue { i64, ptr } %call, 1
; Check if error occurred
%has_error = icmp ne ptr %error, null
br i1 %has_error, label %error_propagate, label %success
error_propagate:
; Propagate error up the stack
%prop = insertvalue { i64, ptr } undef, ptr %error, 1
ret { i64, ptr } %prop
success:
%result = add i64 %value, 10
%ret = insertvalue { i64, ptr } undef, i64 %result, 0
%ret2 = insertvalue { i64, ptr } %ret, ptr null, 1
ret { i64, ptr } %ret2
}
; Trap handler with type matching
define i64 @main() {
entry:
%trap_error = alloca ptr
store ptr null, ptr %trap_error
br label %try_block
try_block:
%call = call { i64, ptr } @compute(i64 10, i64 0)
%value = extractvalue { i64, ptr } %call, 0
%error = extractvalue { i64, ptr } %call, 1
%has_error = icmp ne ptr %error, null
br i1 %has_error, label %trap_landing, label %try_success
trap_landing:
; Load error and check type
%error_typeid = load i64, ptr %error
%type_matches = icmp eq i64 %error_typeid, -3994496327427856726
br i1 %type_matches, label %catch_handler, label %unmatched
catch_handler:
; Extract error value from offset 8
%value_ptr = getelementptr i8, ptr %error, i64 8
%error_value = load i64, ptr %value_ptr
; Free error memory
call void @free(ptr %error)
; Handle error (return -1)
br label %after_trap
unmatched:
; No matching handler - call runtime
call void @__vyn_runtime_untrapped_error(ptr %error)
unreachable
try_success:
br label %after_trap
after_trap:
%result = phi i64 [ %value, %try_success ], [ -1, %catch_handler ]
ret i64 %result
}Happy Path (No Errors):
- Zero allocation: No heap operations when no errors occur
- Zero branching overhead: Modern CPUs predict success path well
- Single comparison:
icmp ne ptr %error, nullper call - Optimal inlining: Small functions inline away error checks
Error Path:
- One malloc: 16-byte allocation for error struct
- Type matching: O(1) hash comparison per trap handler
- Single free: Clean error memory in trap handler
- Stack unwinding: Return through call frames (no exception tables)
Comparison to Alternatives:
| Approach | Success Overhead | Error Overhead | Hidden Control Flow | Compile-time Safety |
|---|---|---|---|---|
| Vyn trap/fail | ~1 comparison | 1 malloc + type match | No | Yes |
| C++ exceptions | Exception tables | Stack unwinding + allocation | Yes | Partial |
| Rust Result<T,E> | Match overhead | Enum size increase | No | Yes |
| Go error returns | Comparison + check | Allocation | No | Weak (can ignore) |
| Java checked exceptions | Exception tables | Stack trace + allocation | Yes | Partial |
1. Use Specific Error Types
# Good: Rich context
struct FileError {
path<String>,
operation<String>,
reason<String>
}
# Less good: Generic integer codes
fail 404
2. Handle Errors Close to Source
# Good: Handle immediately if recovery is possible
result<String> = {
read_file(path)
} trap (e<FileError>) -> {
println("Using default config due to: " + e.reason)
default_config()
}
# Sometimes okay: Let errors propagate if caller should decide
data<String> = read_file(path) # Propagates error to caller
3. Document Error Conditions
# Read configuration from file
#
# Errors:
# FileError { operation = "open", ... } - File doesn't exist
# FileError { operation = "read", ... } - Permission denied
# ParseError { ... } - Invalid TOML format
read_config(path<String>)<Config> -> {
# Implementation...
}
4. Prefer Structured Errors Over Codes
# Good: Self-documenting
struct NetworkError {
url<String>,
status_code<Int>,
retry_after<Int>
}
# Less good: Requires external documentation
fail 503 # What does this mean?
✅ In This Release (v0.4.2):
- fail statements with primitives (Int, Float, String)
- fail statements with custom struct types
- Error heap allocation with type ID + value storage
- Multi-level error propagation through call stacks
- trap blocks with pattern matching on error types
- Struct field access in trap handlers
- Type-safe trap handler matching
- Automatic memory management (malloc/free)
- Untrapped error runtime handler with formatted output
- Complete LLVM codegen for all error operations
ensurekeyword for cleanup/finally blocks- Stack trace capture in error structs (v0.5.1)
- Wildcard trap handlers
} trap (e<?>) -> { ... } - Type introspection (
typeof,as) for discriminating multi-type traps
🔜 Planned Enhancements:
- Error context chaining (wrap errors with additional context)
- Custom error formatting with Display aspect
- Error metrics and telemetry hooks
A comprehensive example showing all error handling features:
# Define domain-specific error types
struct ParseError {
input<String>,
position<Int>,
expected<String>
}
struct ValidationError {
field<String>,
constraint<String>
}
struct DatabaseError {
query<String>,
code<Int>
}
# Parse integer from string
parse_int(s<String>)<Int> -> {
# Actual parsing logic would go here
if (s.len() == 0) {
fail ParseError {
input = s,
position = 0,
expected = "non-empty string"
}
}
return 42 # Simplified
}
# Validate age constraint
validate_age(age<Int>)<Bool> -> {
if (age < 0) {
fail ValidationError {
field = "age",
constraint = "must be non-negative"
}
}
if (age > 150) {
fail ValidationError {
field = "age",
constraint = "must be <= 150"
}
}
return true
}
# Store user in database (can fail)
store_user(name<String>, age<Int>)<Int> -> {
valid<Bool> = validate_age(age) # Propagates ValidationError
# Simulate database operation
if (age == 42) {
fail DatabaseError {
query = "INSERT INTO users",
code = 1062 # Duplicate entry
}
}
return 1 # User ID
}
# Process user registration with comprehensive error handling
register_user(name<String>, age_str<String>)<String> -> {
# Multi-level error handling with different error types
result<String> = {
# Parse age (may fail with ParseError)
age<Int> = parse_int(age_str)
# Store user (may fail with ValidationError or DatabaseError)
user_id<Int> = store_user(name, age)
"User registered with ID: " + String::from_int(user_id)
} trap (e<ParseError>) -> {
msg<String> = "Parse failed: " + e.expected +
" at position " + String::from_int(e.position)
println(msg)
"PARSE_ERROR"
} trap (e<ValidationError>) -> {
msg<String> = "Validation failed for " + e.field +
": " + e.constraint
println(msg)
"VALIDATION_ERROR"
} trap (e<DatabaseError>) -> {
msg<String> = "Database error " + String::from_int(e.code) +
" in query: " + e.query
println(msg)
"DATABASE_ERROR"
}
return result
}
main()<Int> -> {
# Test success path
result1<String> = register_user("Alice", "25")
println(result1) # "User registered with ID: 1"
# Test ParseError
result2<String> = register_user("Bob", "")
println(result2) # "PARSE_ERROR"
# Test ValidationError
result3<String> = register_user("Charlie", "-5")
println(result3) # "VALIDATION_ERROR"
# Test DatabaseError
result4<String> = register_user("Dave", "42")
println(result4) # "DATABASE_ERROR"
return 0
}
Key Takeaways:
- Type Safety: Each error type is distinct and statically checked
- Composability: Errors propagate through multiple function layers
- Clarity: Error handling is explicit in code structure
- Performance: Only allocates memory when errors actually occur
- Ergonomics: Pattern matching makes error handling elegant
The Vyn error handling system achieves the rare combination of safety, performance, and ergonomics that makes robust error handling a joy rather than a chore.
Vyn uses CMake for building:
# Clean build
mkdir -p build && cd build
cmake .. && make clean && make -j
# Quick rebuild (from project root)
make -C build -j
# Run tests
build/vyn test/string/string_test.vynVyn includes a modern, comprehensive test harness for managing 391+ test files:
# Run all tests with parallel execution
./test_harness.py
# Run specific categories
./test_harness.py --category parser,semantic
# Generate comprehensive reports
./test_harness.py --html-report report.html --json-report results.json
# Run with filtering and analysis
./test_harness.py --priority high --exclude-slow --workers 8# Analyze test failures and create triage plan
./triage_tool.py results.json
# Generate markdown triage report
./triage_tool.py results.json --format markdown --output triage.md
# Focus on critical issues only
./triage_tool.py results.json --priority critical,high- 544 Test Files: Comprehensive coverage across all language features
- Parallel Execution: Multi-threaded test runner for fast feedback
- Rich Reporting: HTML, JSON, and console output with detailed metrics
- Smart Categorization: Automatic test categorization and filtering
- Failure Analysis: Pattern recognition and triage plan generation
- Performance Tracking: Execution time analysis and slow test detection
Vyn/
├── src/ # C++ source code
│ ├── main.cpp # Entry point with LLVM JIT
│ ├── lexer.cpp # Tokenization
│ ├── parser.cpp # Syntax analysis
│ └── ast.cpp # Abstract syntax tree
├── include/vyn/ # Header files
├── test/ # Vyn test programs
├── examples/ # Example programs
├── doc/ # Documentation
└── build/ # Build output
Hello World:
main()<Void> -> {
println("Hello, Vyn!")
}
Mathematical Computation:
fibonacci(n<Int>)<Int> -> {
if (n <= 1) {
return n
} else {
return fibonacci(n - 1) + fibonacci(n - 2)
}
}
main()<Int> -> {
return fibonacci(10) # Returns 55
}
Data Processing with Auto-Serialization:
struct Result {
success<Bool>,
value<Int>,
message<String>
}
process_data(x<Int>)<Result> -> {
if (x > 0) {
return Result {
success = true,
value = x * 2,
message = "Processing successful"
}
} else {
return Result {
success = false,
value = 0,
message = "Invalid input"
}
}
}
main()<Result> -> {
return process_data(21)
}
# Outputs structured JSON for the Result
Vyn v0.4.4 includes a complete JSON serialization system with bidirectional conversion between structs and JSON:
struct Person {
name<String>,
age<Int>,
active<Bool>
}
main()<Int> -> {
person<Person> = Person { name: "Alice", age: 30, active: true }
# Serialize to JSON string
json<String> = person.to_string()
println(json) # Output: {"name": "Alice", "age": 30, "active": true}
return 0
}
# Deserialize JSON back to struct
person2<Person> = Person::from_string(json)
# Access fields normally
println(person2.name) # Output: Alice
println(person2.age.to_string()) # Output: 30
- Primitives: Int, Float, Bool, String
- Struct Fields: All primitive types within structs
- Round-Trip: Full struct → JSON → struct → field access
- Type Safety: Runtime validation of JSON structure
One of Vyn's standout features is automatic serialization of complex return types from main():
- Simple integers: Return as exit codes (
main()<Int> -> { return 42 }) - Complex types: Automatically serialize to JSON-like format
- Tuples:
main()<Int,String> -> { return 10, "hello" }outputs[10, "hello"] - Structs: Full structured output with field names and values
The JSON system is built on:
- Runtime Type Metadata: Global type registry with field information
- C Runtime Functions:
__vyn_complex_to_json()and__vyn_complex_from_json() - Type Registration: Automatic registration via global constructors
- String Conversion: Seamless char* to VynString{data, length} struct conversion
See test/json/ for comprehensive examples and runtime/vyn_type_metadata.c for implementation.
This makes Vyn excellent for data processing scripts, API services, and configuration management.
Vyn provides multiple memory management strategies:
# Unique ownership (like Rust's Box)
owned<my<String>> = my("unique data")
# Shared ownership (reference counted)
shared<our<String>> = our("shared data")
another_ref<our<String>> = shared # Reference count incremented
# Borrowing (non-owning references)
view_ref<their<String>> = view(shared) # Immutable borrow
mut_ref<their<String>> = borrow(owned) # Mutable borrow
# Raw pointers for freedom operations
freedom {
x<Int> = 42
ptr<loc<Int>> = loc(x) # Get pointer to x
at(ptr) = 99 # Modify through pointer
}
Vyn v0.4.2 (freedom-1.0) is a fully functional, production-ready systems programming language. Future enhancements:
- Self-Hosted Standard Library: Pure Vyn stdlib implementation
- Complete String Implementation: UTF-8 support, interpolation, advanced methods
- Import/Smuggle System: Module visibility and dependency management
- C Bindings (FFI): Foreign function interface for C interoperability
- Range Patterns:
1..10 -> "range"syntax in match/select (design conflicts with comparison patterns) - Package Scoping: Module system and package management architecture
- Enhanced Async/Await: Full threading, cancellation points, timed operations
- Self-Hosting: Vyn compiler written in Vyn
- Package Manager: Dependency resolution and registries
- Advanced Optimizations: LLVM optimization passes and compile-time improvements
Current Status: All core language features are complete and working. See doc/ROADMAP.md for detailed development plans.
Vyn v0.4.2 includes a modern, comprehensive testing infrastructure designed for efficient development and quality assurance:
The new Python-based test harness provides enterprise-grade testing capabilities:
# Basic test run with parallel execution
python3 test_harness.py --parallel
# Full test suite with HTML reporting and triage analysis
python3 test_harness.py --parallel --html-report --triage --performance
# Test specific patterns or directories
python3 test_harness.py --filter "async" --verbose
python3 test_harness.py --directory test/units --timeout 30- Parallel Execution: Multi-threaded test running for faster feedback
- Rich Reporting: HTML reports with test results, timing, and failure analysis
- JSON Export: Machine-readable test data for CI/CD integration
- Triage Analysis: Automatic failure pattern recognition and prioritization
- Performance Tracking: Test execution timing and performance regression detection
- Flexible Filtering: Run specific tests, directories, or patterns
- Progress Tracking: Real-time progress bars and status updates
- Error Context: Detailed failure information with context and suggestions
- Total Tests: 391+ comprehensive test cases
- Coverage Areas: Language features, edge cases, error conditions, performance
- Test Types: Unit tests, integration tests, syntax validation, runtime verification
- Success Rate: >95% pass rate maintained across all features
Automated tools ensure codebase consistency and syntax standardization:
# Scan for legacy syntax patterns
python3 migrate_syntax.py --scan --directory . --report
# Apply canonical syntax migrations with backup
python3 migrate_syntax.py --migrate --directory . --backup- Legacy Detection: Identifies
make_my(),make_our(), and function-call borrowing - Automatic Conversion: Converts to canonical
my(),our(),view,borrowsyntax - Backup Creation: Preserves original files before modification
- Comprehensive Reporting: Detailed migration reports with change summaries
- Pattern Recognition: Smart detection of syntax inconsistencies across file types
Automated failure analysis and development prioritization:
# Generate triage report from test results
python3 triage_tool.py test_results.json --output triage_report.html- Pattern Recognition: Groups similar failures for efficient debugging
- Priority Assignment: Ranks issues by impact and frequency
- Root Cause Analysis: Identifies common failure patterns
- Debugging Recommendations: Suggests investigation strategies
- Progress Tracking: Monitors issue resolution over time
The integrated toolchain supports efficient development:
- Write Code: Use canonical syntax with ownership types
- Run Tests:
python3 test_harness.py --parallel --triage - Check Syntax:
python3 migrate_syntax.py --scan - Debug Issues: Use triage reports for prioritized debugging
- Commit Changes: Regular Git commits with test validation
Vyn is built on solid foundations:
- Frontend: Hand-written recursive descent parser
- AST: Rich abstract syntax tree with source location tracking
- Backend: LLVM for code generation and ORC JIT execution
- Type System: Strong static typing with canonical ownership syntax
- Runtime: LLVM ORC JIT (LLJIT) with auto-serialization support
- Test Infrastructure: Modern parallel test harness with comprehensive reporting
Vyn is actively developed with regular commits tracking progress:
- Language Features: Add new syntax, types, or operations
- Standard Library: Implement core modules and utilities
- Testing: Create comprehensive test cases
- Documentation: Improve guides and examples
- Performance: Optimize compilation and runtime
See doc/ directory for detailed design documents and RFCs.
v0.4.2 (freedom-1.0 series): Generic function monomorphization and FREEDOM blocks
- ✅ Generic Functions: Complete LLVM monomorphization system for generic functions with bounded type parameters
- Type Parameter Substitution:
T → Pointduring codegen - On-Demand Instantiation: Generate specialized functions like
printItem_Pointfrom templates - Method Resolution: Call aspect methods on generic parameters (
item.show()whereitem: T<Display>) - Caching: Reuse monomorphized functions for same type combinations
- Integration: Works seamlessly with aspects, traits, and generic structs
- Type Parameter Substitution:
- ✅ Select Expressions: Pattern matching expressions with
select(expr) -> { pattern -> result };syntax- Naked expressions: Simple values auto-return without
passkeyword - Complex blocks: Use
passkeyword to return from block without exiting function - Type inference: First case determines result type for entire select
- Pattern matching: Exact equality patterns with wildcard
?support
- Naked expressions: Simple values auto-return without
- ✅ Canonical Syntax Unification: Complete migration to unified
my()/our()constructors andview/borrowoperators - ✅ Modern Test Harness: Parallel test runner managing 391+ tests with HTML/JSON reporting and failure triage
- ✅ Syntax Migration Tools: Automated migration from legacy to canonical syntax with comprehensive reporting
- ✅ Match Statements: Complete pattern matching with
->arrow syntax and?wildcard; no-match results in NOP - ✅ Break/Continue: Loop control flow statements working in all loop types
- ✅ Vec Collections: Fully functional resizable arrays with all methods (
new,push,pop,len,get) - ✅ Modern Struct Syntax: Updated to
field<Type>syntax with perfect field access - ✅ Complete Binary Operations: All arithmetic, comparison, and logical operators
- ✅ Dual Parameter Syntax: Both
var<Type> nameandType nameforms working seamlessly - ✅ Member Access: Object field access (
obj.field) and array indexing (arr[index]) - ✅ Auto-serialization: Complex return types with smart JSON-like output
- ✅ Async/Await: Complete asynchronous programming support with Future types
- ✅ Debug Infrastructure: Full LLVM debug metadata with async state machine debugging
Language Status: Vyn v0.4.2 (freedom-1.0 series) is a complete, production-ready systems programming language with unified canonical syntax, comprehensive sized type system (Int8-Int64, UInt8-UInt64, Float32/64, Char, Rune, Bytes), generic function monomorphization, comprehensive test infrastructure, and advanced debugging capabilities, suitable for real-world programming tasks with all core language constructs implemented, tested, and fully consistent.
- Documentation: See
doc/for language design and internals - Examples: Check
examples/for working programs - Tests: Review
test/for feature demonstrations - Issues: Report bugs or request features on GitHub
Vyn's syntax is defined by a comprehensive EBNF grammar reflecting v0.4.2 capabilities:
// Conventions:
// IDENTIFIER: Represents a valid identifier token.
// INTEGER_LITERAL: Represents an integer literal token.
// FLOAT_LITERAL: Represents a float literal token.
// STRING_LITERAL: Represents a string literal token.
// BOOLEAN_LITERAL: Represents 'true' or 'false'.
// 'keyword': Denotes a literal keyword.
// { ... }: Represents zero or more occurrences (Kleene star).
// [ ... ]: Represents zero or one occurrence (optional).
// ( ... | ... ): Represents a choice (alternation).
// ... ::= ... : Defines a production rule.
// Module Structure
module ::= { module_item } EOF
module_item ::= import_statement
| smuggle_statement
| struct_declaration
| enum_declaration
| bind_declaration
| function_declaration
| variable_declaration
| constant_declaration
| type_alias_declaration
| aspect_declaration
| statement
// Import System (import/smuggle)
import_statement ::= 'import' path [ 'as' IDENTIFIER ] [';']
smuggle_statement ::= 'smuggle' path [ 'as' IDENTIFIER ] [';']
path ::= IDENTIFIER { ('::' | '.') IDENTIFIER }
// Type Declarations
struct_declaration ::= [ 'pub' ] [ 'template' '<' type_parameter_list '>' ]
'struct' IDENTIFIER '{' { struct_field_declaration } '}'
struct_field_declaration ::= [ 'pub' ] IDENTIFIER '<' type '>' [ '=' expression ] [';']
field_declaration ::= [ 'pub' ] IDENTIFIER '<' type '>' [ '=' expression ] [';']
enum_declaration ::= [ 'pub' ] [ 'template' '<' type_parameter_list '>' ]
'enum' IDENTIFIER '{' { enum_variant } '}'
enum_variant ::= IDENTIFIER [ '(' type_list ')' ] [ '=' expression ] ','?
bind_declaration ::= [ 'template' '<' type_parameter_list '>' ]
'bind' type [ '->' type ] '{' { method_declaration } '}'
aspect_declaration ::= [ 'pub' ] 'aspect' IDENTIFIER [ template_parameters ]
'{' { method_signature } '}'
// Function Declarations
function_declaration ::= [ 'pub' ] [ 'template' '<' type_parameter_list '>' ] [ 'async' ]
IDENTIFIER '(' [ parameter_list ] ')' '<' type_list '>' '->'
( block_statement | expression [';'] | statement )
[ 'throws' type_list ]
method_declaration ::= [ 'pub' ] [ 'static' ] [ 'template' '<' type_parameter_list '>' ] [ 'async' ]
IDENTIFIER '(' [ parameter_list ] ')' '<' type '>' '->'
( block_statement | expression [';'] ) [ 'throws' type_list ]
method_signature ::= [ 'async' ] IDENTIFIER '(' [ parameter_list ] ')'
'<' type_list '>' '->' ';' [ 'throws' type_list ]
constructor_declaration::= [ 'pub' ] 'new' [ template_parameters ]
'(' [ parameter_list ] ')' [ 'throws' type_list ]
( block_statement | '=>' expression [';'] )
// Variable Declarations
variable_declaration ::= [ 'pub' ] IDENTIFIER '<' type '>' [ '=' expression ] [';']
constant_declaration ::= [ 'pub' ] IDENTIFIER '<' type 'const' '>' '=' expression [';']
type_alias_declaration ::= [ 'pub' ] 'type' IDENTIFIER [ template_parameters ] '=' type [';']
// Parameters and Templates
type_parameter_list ::= type_parameter { ',' type_parameter }
type_parameter ::= IDENTIFIER [ ':' type_bounds ] | expression
type_bounds ::= type { '+' type }
template_parameters ::= '<' type_parameter_list '>'
parameter_list ::= parameter { ',' parameter }
parameter ::= [ 'const' ] IDENTIFIER '<' type '>' [ '=' expression ]
type_list ::= type { ',' type }
// Statements
statement ::= expression_statement
| block_statement
| if_statement
| for_statement
| while_statement
| loop_statement
| match_statement
| return_statement
| break_statement
| continue_statement
| pass_statement
| defer_statement
| try_statement
| variable_declaration
| constant_declaration
| pattern_assignment_statement
| scoped_statement
| throw_statement
expression_statement ::= expression [';']
block_statement ::= '{' { statement } '}'
if_statement ::= 'if' expression ( block_statement | statement_without_block )
{ 'else' 'if' expression ( block_statement | statement_without_block ) }
[ 'else' ( block_statement | statement_without_block ) ]
for_statement ::= 'for' pattern 'in' expression block_statement
while_statement ::= 'while' expression block_statement
loop_statement ::= 'loop' block_statement
match_statement ::= 'match' '(' expression ')' '{' match_arm* '}'
match_arm ::= pattern '->' ( expression | block_statement | statement_without_block ) ','?
select_expression ::= 'select' '(' expression ')' '->' '{' select_arm* '}' ';'
select_arm ::= pattern '->' ( expression | block_statement ) ','?
return_statement ::= 'return' [ expression ] [';']
break_statement ::= 'break' [ IDENTIFIER ] [ expression ] [';']
continue_statement ::= 'continue' [ IDENTIFIER ] [';']
pass_statement ::= 'pass' expression [';']
defer_statement ::= 'defer' ( expression_statement | block_statement )
throw_statement ::= 'throw' expression [';']
scoped_statement ::= 'scoped' block_statement
try_statement ::= 'try' block_statement { trap_clause } [ 'finally' block_statement ]
trap_clause ::= 'trap' '(' IDENTIFIER '<' type '>' ')' '->' block_statement
pattern_assignment_statement ::= pattern '=' expression [';']
statement_without_block ::= expression_statement | return_statement | break_statement
| continue_statement | throw_statement
// Patterns
pattern ::= comparison_pattern
| IDENTIFIER [ '@' pattern ]
| literal
| '?'
| path '{' [ field_pattern { ',' field_pattern } [','] ] '}'
| path '(' [ pattern_list ] ')'
| '[' [ pattern_list ] ']'
| '(' pattern_list ')'
| '&' [ 'const' ] pattern
comparison_pattern ::= ( '==' | '!=' | '<' | '<=' | '>' | '>=' ) expression
field_pattern ::= IDENTIFIER ':' pattern | IDENTIFIER
pattern_list ::= pattern { ',' pattern }
// Expressions
expression ::= assignment_expression
assignment_expression ::= conditional_expression [ assignment_operator assignment_expression ]
assignment_operator ::= '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>='
conditional_expression ::= logical_or_expression [ '?' expression ':' conditional_expression ]
| if_expression
logical_or_expression ::= logical_and_expression { '||' logical_and_expression }
logical_and_expression ::= bitwise_or_expression { '&&' bitwise_or_expression }
bitwise_or_expression ::= bitwise_xor_expression { '|' bitwise_xor_expression }
bitwise_xor_expression ::= bitwise_and_expression { '^' bitwise_and_expression }
bitwise_and_expression ::= equality_expression { '&' equality_expression }
equality_expression ::= relational_expression { ( '==' | '!=' ) relational_expression }
relational_expression ::= range_expression { ( '<' | '<=' | '>' | '>=' | 'is' | 'as' ) range_expression }
range_expression ::= shift_expression [ '..' shift_expression ]
shift_expression ::= additive_expression { ( '<<' | '>>' ) additive_expression }
additive_expression ::= multiplicative_expression { ( '+' | '-' ) multiplicative_expression }
multiplicative_expression ::= unary_expression { ( '*' | '/' | '%' ) unary_expression }
unary_expression ::= ( '!' | '-' | '+' | '*' | 'await' | 'throw' ) unary_expression
| primary_expression
primary_expression ::= literal
| path_expression
| '(' expression ')'
| call_expression
| member_access_expression
| index_access_expression
| list_comprehension
| array_literal
| array_construction
| tuple_literal
| struct_literal
| lambda_expression
| select_expression
| 'self' | 'super'
if_expression ::= 'if' expression block_statement 'else' ( block_statement | if_expression )
literal ::= INTEGER_LITERAL | FLOAT_LITERAL | STRING_LITERAL | BOOLEAN_LITERAL | 'null'
path_expression ::= path [ type_arguments ]
call_expression ::= primary_expression '(' [ argument_list ] ')' [ '?' ]
argument_list ::= expression { ',' expression }
member_access_expression ::= primary_expression ( '.' | '?.' | '::' ) IDENTIFIER
index_access_expression ::= primary_expression '[' expression ']' [ '?' ]
list_comprehension ::= '[' expression 'for' pattern 'in' expression [ 'if' expression ] ']'
array_literal ::= '[' [ expression { ',' expression } [','] ] ']'
| '[' expression ';' expression ']'
array_construction ::= ArrayType '(' ')'
tuple_literal ::= '(' [ expression { ',' expression } [ ',' ] ] ')'
struct_literal ::= [ path_expression ] '{' [ struct_literal_field { ',' struct_literal_field } [ ',' ] ] '}'
struct_literal_field ::= IDENTIFIER (':' | '=') expression | IDENTIFIER
lambda_expression ::= [ 'async' ] ( '|' [ parameter_list ] '|' | IDENTIFIER ) '<' type '>'
( '=>' expression | block_statement )
// Type System
Type ::= BaseType [ 'const' ] [ '?' ]
BaseType ::= IDENTIFIER
| OwnershipWrapper '<' Type '>'
| ArrayType
| TupleType
| FunctionType
| '(' Type ')'
OwnershipWrapper ::= 'my' | 'our' | 'their' | 'ptr' | 'loc'
ArrayType ::= '[' Type [ ';' Expression ] ']'
TupleType ::= '(' [ Type { ',' Type } [ ',' ] ] ')'
FunctionType ::= [ 'async' ] '(' [ Type { ',' Type } ] ')' '<' Type '>' '->'
[ 'throws' TypeList ]
type_arguments ::= '<' type_argument_list '>'
type_argument_list ::= type_argument { ',' type_argument }
type_argument ::= Type | Expression
// Borrowing Intrinsics
BorrowExpr ::= 'borrow' '(' Expression ')'
| 'view' '(' Expression ')'Key EBNF Features:
- Unified Syntax: All declarations use
name<Type>pattern - Comparison Patterns:
>= expr,<= expr, etc. for match/select - Select Expressions: Pattern matching that returns values with
passkeyword - Ownership Types:
my<T>,our<T>,their<T>,mild<T>,loc<T>wrappers - Import/Smuggle: Dual module system for verified vs. flexible imports
- Async/Await: Full async function and expression support
- Multiple Returns: Functions can return multiple values (tuples)
Ownership Types:
my<T>: Unique ownership, RAII cleanupour<T>: Shared ownership, reference countedtheir<T>: Non-owning borrow, lifetime checkedmild<T>: Mild reference, can detect destruction viagrab()andreleased()
Borrowing Operations:
view(expr): Createstheir<T const>immutable borrowborrow(expr): Createstheir<T>mutable borrowsoft(expr): Createsmild<T>mild reference fromour<T>
Freedom Operations:
loc<T>: Raw pointer typeloc(expr): Get pointer to expressionat(ptr): Dereference pointer (read/write)from<loc<T>>(expr): Pointer type conversion
Vyn automatically serializes complex return types from main():
Simple Returns:
main()<Int> -> { return 42 }→ Exit code 42main()<String> -> { return "hello" }→ Outputs: hello
Complex Returns:
main()<Int,String> -> { return 42, "hello" }→ Outputs: [42, "hello"]- Struct returns → JSON with field names and values
- Vec returns → JSON array representation
Customization:
Implement Serialize aspect for custom serialization behavior.
AST (Abstract Syntax Tree) Tree representation of source code structure produced by the parser.
Borrow Checking Compile-time analysis ensuring references don't outlive the data they point to.
Bundle Planned namespace grouping for fine-grained module visibility control.
Import Secure module inclusion from verified, signed sources.
JIT (Just-In-Time) Runtime compilation of code to native machine instructions for performance.
LLVM Low Level Virtual Machine - compiler infrastructure used by Vyn's backend.
Monomorphization Compile-time process of creating specialized versions of generic functions/types.
Ownership
Memory management system using my<T>, our<T>, their<T>, mild<T> types.
Pattern Matching
match statements that destructure and test values against patterns.
Smuggle Flexible module inclusion from external, potentially unverified sources.
Template Compile-time generic construct parameterized by types or constants.
Vec
Resizable array collection with methods like push(), pop(), len(), get().
Apache License - see LICENSE file for details.
Vyn v0.4.2 (freedom-1.0 series): A complete systems programming language with comprehensive sized type system, unified syntax, pattern matching, generic functions, resizable collections, and unique import/smuggle module system - ready for real-world development.
