Releases: rufflang/ruff
0.10.0
Added
-
Optional Static Typing Design Package (Exploratory, v0.10.0):
- Added consolidated design document at
docs/OPTIONAL_TYPING_DESIGN.mdcovering:- annotation surface proposal for function signatures, variable declarations, and generic collection forms
- optional runtime type-check mode contract and deterministic type-error shape targets
- typed-JIT optimization boundaries, explicit non-goals, migration/compatibility posture, and open decisions
- Added consolidated design document at
-
Native Bulk SSG Rendering Builtin (P0 Optimization step):
- Added
ssg_render_pages(source_pages)native function for high-volume static page wrapping - Returns a dictionary with:
pages: rendered HTML page arraychecksum: aggregate output checksum for benchmark equivalence checks
- Updated
benchmarks/cross-language/bench_ssg.ruffto use native bulk rendering beforeasync_write_files(...) - Added comprehensive validation tests in
src/interpreter/native_functions/strings.rsfor:- valid page rendering + checksum behavior
- non-array input errors
- non-string page element errors
- invalid argument count errors
- Measured Ruff-only
bench-ssg --profile-asyncimpact (10,000 files, local run):- Before native rendering:
33,107.557 ms(302.05 files/sec) - After native rendering:
23,178.914 ms(431.43 files/sec) - Improvement: ~
29.99%faster total build time in this benchmark path
- Before native rendering:
- Added
-
Bulk Async File I/O Builtins for SSG-Scale Workloads (P0 Optimization step):
- Added
async_read_files(paths, concurrency_limit?)for bounded concurrent multi-file reads - Added
async_write_files(paths, contents, concurrency_limit?)for bounded concurrent multi-file writes - Added comprehensive native-function coverage for:
- ordered multi-file read behavior
- full multi-file write + output verification
- input validation (path/content element types, array length mismatch, invalid concurrency limits)
- propagated I/O error behavior for missing files
- Updated
benchmarks/cross-language/bench_ssg.ruffto use bulk async APIs instead of per-file promise arrays - Measured impact on
ruff bench-ssg(10,000 files, local run):- Before:
57,685.809 ms(173.35 files/sec) - After:
51,713.739 ms(193.37 files/sec) - Improvement: ~
10.35%faster Ruff build time in this benchmark path
- Before:
- Added
-
Spawn Parent-Binding Snapshot Capture (P0 shared-state milestone):
-
spawn { ... }now receives a transferable snapshot of parent bindings at spawn time -
Spawned workers can reference parent-defined scalar/collection values (for example shared-store keys)
-
Preserves non-blocking spawn semantics while keeping parent scope write-back isolated
-
Added comprehensive integration tests covering:
- scalar capture visibility inside spawned workers
- parent-defined shared-key usage from spawned workers
- no write-back of spawned local mutations into parent scope
-
Example usage:
counter_key := "jobs_counter"shared_set(counter_key, 0)
for i in range(0, 12) {
spawn {
shared_add_int(counter_key, 1)
}
}
-
-
Thread-Safe Shared Value Operations for Spawn Concurrency (P0 Phase 1 completion):
-
Added process-wide thread-safe shared value builtins:
shared_set(key, value)shared_get(key)shared_has(key)shared_delete(key)shared_add_int(key, delta)
-
Shared values are synchronized with
Arc<Mutex<...>>storage so updates are safe across spawned interpreters -
Enables practical cross-thread coordination despite
spawnusing isolated environments -
Added comprehensive integration tests for:
- shared value lifecycle (set/get/has/delete)
- atomic-style integer accumulation with validation error paths
spawnworkers updating a shared counter across isolated interpreters
-
Example usage:
shared_set("counter", 0)for i in range(0, 20) {
spawn {
shared_add_int("counter", 1)
}
}
-
-
10K+ Concurrency Scalability Testing (Phase 3 Completion):
-
Added comprehensive scalability test example (
examples/test_scalability_10k.ruff) -
Tests
parallel_map,promise_all, andpar_eachwith 10,000 concurrent operations -
All scalability tests complete successfully in <3 seconds
-
Added three integration tests to test suite:
test_parallel_map_scalability_10k: 10K items withparallel_maptest_promise_all_scalability: 10K items across 10 promises withpromise_alltest_par_each_scalability: 10K items withpar_each
-
Completes Phase 3 roadmap item: "Test scalability with 10K+ concurrent operations"
-
Example usage:
items := range(0, 10000)result_promise := parallel_map(items, func(x) { return x * x }, 100)
results := await result_promiseProcesses 10,000 items in <1 second
-
-
Async SSG Bottleneck Profiling (P0 Optimization step):
- Added
--profile-asyncflag toruff bench-ssgfor per-stage timing breakdown output bench-ssgnow reports read-stage and render/write-stage timings when benchmark scripts emit profiling metrics- Added bottleneck summary output (largest stage + profiled-time share)
- Extended SSG benchmark artifacts to emit stage metrics:
- Ruff script emits
RUFF_SSG_READ_MSandRUFF_SSG_RENDER_WRITE_MS - Python baseline emits
PYTHON_SSG_READ_MSandPYTHON_SSG_RENDER_WRITE_MS
- Ruff script emits
- Added comprehensive parser/profile tests in
src/benchmarks/ssg.rsfor optional metric parsing and bottleneck-stage calculations
- Added
-
Promise Creation/Resolution Overhead Optimization (P0 Optimization step):
- Optimized
parallel_map(...)mixed-result path to avoid creating synthetic oneshot-backed promises for immediate mapper values parallel_map(...)now:- preserves immediate results directly in preallocated output slots
- awaits only real promise receivers with bounded in-task concurrency
- resolves immediately when all mapper outputs are non-promise values
- Reduced allocation/churn in
Promise.all(...)hot path by preallocating receiver/future vectors and reusing a single debug flag check - Added integration coverage for mixed immediate/promise mapper behavior and immediate-only fast path
- Optimized
-
Cached Await Reuse for Frequently-Awaited Operations (P0 Optimization step):
- Added cache-aware promise aggregation in
Promise.all(...)andparallel_map(...) - Already-polled promises now resolve from cached results without re-consuming oneshot receivers
- Aggregation paths now persist resolved/rejected outcomes back into each source promise cache to accelerate repeated awaits
- Added integration coverage for:
- reusing a previously awaited promise in
promise_all([p, p], ...) - reusing a cached promise returned by a
parallel_map(...)mapper
- reusing a previously awaited promise in
- Added cache-aware promise aggregation in
-
Cross-Language Async SSG Benchmark Harness (P0 Option 1 validation):
- Added
ruff bench-ssgcommand to execute a reproducible 10,000-file async SSG workload in Ruff - Added optional Python baseline comparison via
ruff bench-ssg --compare-python - Added benchmark artifacts:
benchmarks/cross-language/bench_ssg.ruffbenchmarks/cross-language/bench_ssg.py
- Added benchmark parser/validator module at
src/benchmarks/ssg.rswith checksum + file-count equivalence checks - Added comprehensive unit tests for metric parsing and speedup calculations
- Latest baseline run (
ruff bench-ssg --compare-python):- Ruff:
73,392.770 ms(136.25 files/sec) - Python:
4,390.775 ms(2,277.50 files/sec) - Relative speed: Ruff
0.06xvs Python baseline
- Ruff:
- Added
-
Async VM Cooperative Scheduler (P0 Option 1 step):
- Added scheduler APIs in
src/vm.rsfor managing multiple suspended VM contexts:run_scheduler_round()run_scheduler_until_complete(max_rounds)pending_execution_context_count()
- Added
VmSchedulerRoundResultwith per-round completion and pending counts resume_execution_context(context_id)now removes completed contexts automatically to prevent stale context accumulation- Added comprehensive VM tests for:
- scheduler completion cleanup semantics
- multi-context scheduler completion
- pending-round reporting for unready promises
- scheduler round-budget exhaustion and validation errors
- Added scheduler APIs in
-
Cooperative Await Yield/Resume Execution (P0 Option 1 step):
- Added cooperative VM execution APIs in
src/vm.rs:execute_until_suspend(chunk)resume_execution_context(context_id)
- Added
VmExecutionResultto report cooperative execution state (CompletedvsSuspended) OpCode::Awaitnow supports non-blocking cooperative polling viatry_recv()and suspends execution contexts instead of blocking when cooperative mode is enabled- Suspended await contexts now preserve state by rewinding IP to re-run
Awaiton resume and snapshotting the active context - Added VM tests covering:
- suspension/resume flow for pending async await
- cooperative completion flow with no suspension
- Added cooperative VM execution APIs in
-
Async VM Execution Context Switching Foundation (P0 Option 1 step):
- Added stable VM context IDs via
VmContextId - Added VM context lifecycle APIs in
src/vm.rs:- `create_executi...
- Added stable VM context IDs via
0.9.0
Added
-
Hash Map Loop Fusion Optimization (2026-02-12):
- Added fused bytecode opcodes for canonical int-key map loops:
SumIntMapUntilLocalInPlaceforsum := sum + map[i]read loopsFillIntMapWithDoubleUntilLocalInPlaceformap[i] := i * 2write loops
- Added compiler pattern matching/lowering for both loop forms
- Added VM execution handlers for both fused opcodes
- Added targeted VM tests for fused sum loop success and missing-key error paths
- Verified cross-language benchmark improvement:
- Ruff hash map benchmark:
0.56078 ms - Python hash map benchmark:
33.25 ms
- Ruff hash map benchmark:
- Added fused bytecode opcodes for canonical int-key map loops:
-
JIT Stability Regression Fix (2026-02-12):
- Fixed Cranelift block sealing regressions in JIT compile paths
- Restored full test-suite stability (
cargo testall passing) - Revalidated cross-language benchmarks after the fix
-
String Concatenation Optimization + Stability Validation (2026-02-12):
- Added dedicated bytecode append opcodes for constant string/char in-place updates
- Added compiler lowering for
x := x + "literal"and loop fusion for canonical concat loops - Added VM handlers for in-place append and fused append-until-local execution
- Added JIT helper wiring for append in-place paths and translator opcode support
- Fixed regressions and warning sources uncovered during optimization work
- Validated with green
cargo test --releaseand updated cross-language benchmark run
-
Phase 7: Function-Level JIT Implementation (v0.9.0) - IN PROGRESS:
-
Step 11: Loop Back-Edge Fix (2026-01-30):
- Fixed JIT compilation of loops with backward jumps (JumpBack opcode)
- Added
analyze_loop_headers()to pre-detect backward jump targets - Added
stack_effect()to calculate stack depth changes per opcode - Added
loop_header_pcsHashSet to track loop headers - Loop headers are now created with proper Cranelift block parameters
- Loop headers are sealed AFTER back-edges are processed (late sealing)
- JumpBack now correctly passes stack values as block arguments
- Added comprehensive test
test_compile_simple_loopfor validation - All JIT tests (31) passing with loop support
- Note: Loops in top-level scripts still run interpreted (only functions JIT-compile)
-
Step 12 Partial: Direct JIT Recursion (2026-01-30):
- Added
CompiledFnWithArgtype for functions that take args directly - Added
CompiledFnInfostruct tracking standard + direct-arg variants - Implemented
function_has_self_recursion()for bytecode analysis - Implemented
compile_function_with_direct_arg()Cranelift compilation - Implemented
translate_direct_arg_instruction()for direct-arg mode - Updated interpreter to prefer direct-arg variant for 1-int-arg calls
- Self-recursion detection correctly identifies recursive patterns
- Infrastructure complete but performance optimization needs debugging
- Note: Direct-arg functions being called but not achieving expected speedup
- Added
-
Step 10 Partial: Fast Argument Passing (2026-01-29):
- Implemented fast argument passing via VMContext.argN fields
- Added arg0-arg3 and arg_count fields to VMContext struct
- Added
jit_get_arg()runtime helper for parameter access - JIT now reads parameters from VMContext.argN (≤4 integer args)
- Eliminated var_names HashMap clone on recursive calls
- Skipped HashMap population for simple integer functions
- Performance improved from ~1.03s to ~0.81s for fib(25) (~20% faster)
- Note: Still 29x slower than Python (~28ms) due to call overhead
- Architecture limitation: Each JIT call goes through FFI boundary
- Future work needed: True direct JIT-to-JIT recursion requires changing function signature to pass args directly in Cranelift
-
Step 9 Complete: Inline Caching for Function Calls (2026-01-28):
- Implemented inline caching to accelerate repeated function calls
- Added
CallSiteIdstruct to uniquely identify call sites by (chunk_id, ip) - Added
InlineCacheEntrystruct with cached function pointer and var_names - Call sites now cache:
- JIT-compiled function pointer (no HashMap lookup after first call)
- Pre-computed var_names HashMap (avoids rebuilding on every call)
- Guard validation via function name comparison
- Eliminated var_names HashMap clone in inline cache fast path
- Added hit/miss counters for profiling and debugging
- Added comprehensive test suite (
tests/jit_inline_cache.ruff):- Test 1: Simple function called 200 times (cache warm-up)
- Test 2: Recursive Fibonacci with inline cache
- Test 3: Nested function calls (multiple call sites)
- Test 4: Different functions (guard validation)
- Test 5: Function with local variables
- Test 6: Higher-order function pattern (pre-JIT)
- Test 7: Deep call chain (4 levels)
- Test 8: Zero-parameter function
- All 198 unit tests passing
- Phase 7 now 90% complete (Steps 1-9 of 10)
- Note: Discovered JIT limitation with higher-order functions (functions as arguments)
- Next: Step 10 - Value Unboxing
-
Step 8 Complete: Return Value Optimization (2026-01-28):
- Implemented fast return value path that bypasses VM stack operations
- Added
return_valueandhas_return_valuefields to VMContext struct - Added
jit_set_return_int()runtime helper for optimized integer returns - Modified Return opcode translation to use fast path when available
- Updated VM to check
has_return_valuebefore reading from stack - Fallback to stack-based returns for non-integer types preserved
- Added comprehensive test:
test_return_value_optimization - Optimization reduces overhead for integer returns by:
- Eliminating stack push/pop operations for return values
- Avoiding Value::Int boxing at the JIT/VM boundary
- Direct memory access to VMContext.return_value field
- All 198 unit tests passing
- Phase 7 now 80% complete (Steps 1-8 of 10)
- Next: Step 9 - Iterative Fibonacci & Full Benchmarks
-
Step 7 Complete: Register-Based Locals (2026-01-28):
- Implemented register-based locals optimization for JIT-compiled functions
- Added
local_slotsHashMap to BytecodeTranslator for stack slot mapping - Implemented
allocate_local_slots()to pre-allocate Cranelift stack slots - Implemented
allocate_parameter_slots()for function parameters - Implemented
initialize_parameter_slots()to copy params from HashMap to slots - LoadVar/StoreVar now use fast stack slots for local variables
- Fall back to runtime calls for globals and function references
- Added comprehensive test suite (
tests/jit_register_locals.ruff) - All 198 unit tests passing
- Local variables now use direct memory access instead of:
- C function calls for every variable access
- HashMap lookups at runtime
- Phase 7 now 70% complete (Steps 1-7 of 10)
- Next: Step 8 - Inline Caching for function calls
-
Step 5 Complete: Testing & Validation (2026-01-28):
- Created comprehensive test suite for JIT function calls
- Test nested function calls (2-4 levels deep): All passing
- Test iterative fibonacci: Correct results for fib(0) to fib(20)
- Test recursive fibonacci: Faster than Python for fib(20) (101ms vs 168ms)
- Test edge cases: 0 params, 5+ params, local variables, recursion
- Added test files: test_nested_simple.ruff, test_fib_simple.ruff, test_fib_rec_simple.ruff, test_edges_simple.ruff
- All 198 unit tests still passing
- Verified JIT compilation triggers correctly after 100 calls
- Confirmed correct stack and locals handling in nested calls
- Phase 7 now 50% complete (Steps 1-5 of 10)
- Next: Step 6 - Recursive Function Optimization
- Performance validation shows JIT is working as designed!
-
Step 4 Complete: Argument Passing Optimization (2026-01-28):
- Implemented jit_push_int runtime helper for return value handling
- Implemented call_function_from_jit in VM for function execution from JIT
- Added proper locals binding with parameter name → value mapping
- Added var_names HashMap for variable name resolution via hashing
- LoadVar/StoreVar now work correctly in JIT-compiled functions
- Functions can now call other functions with full argument passing
- Return values properly pushed to VM stack as Value::Int
- Test examples working: identity(x), add(a,b), nested calls
- All 79 tests still passing
- Major milestone: JIT functions are now truly functional!
- Next: Step 5 - Testing & Validation
-
Step 3 Complete: Call Opcode JIT Support (2026-01-28):
- Added
jit_call_functionruntime helper for function calls from JIT code - Added Call opcode translation in
BytecodeTranslator::translate_instruction - Updated
is_supported_opcodeto includeOpCode::Call - Added
call_funcfield and setter to BytecodeTranslator - Declared jit_call_function symbol in JitCompiler::new
- Functions containing Call opcodes can now be JIT-compiled!
- Runtime execution is placeholder (Step 4 will implement actual execution)
- All 79 tests...
- Added
-
0.8.0
Added
- Async/Await ⚡ - Full asynchronous programming support with Promise-based concurrency (P1 feature - COMPLETE):
- Async Functions: Declare functions with
async funcsyntax - Await Expression: Pause execution with
await promise_valueuntil Promise resolves - Promise Type: New built-in type for representing asynchronous operations
- Thread-Based Runtime: Async functions execute in separate threads for true concurrency
- Channel Communication: Promises use mpsc channels for thread-safe result passing
- Thread-Safe Architecture: Complete Arc<Mutex<>> refactor replacing Rc<RefCell<>> throughout codebase
- Features:
- Async function declarations create Promises automatically
- Await expressions properly block and retrieve results
- Error handling with try/except in async contexts
- Compatible with existing concurrency (spawn blocks, channels)
- Generator compatibility maintained
- Examples:
# Async function declaration async func fetch_data(id) { let result := simulate_api_call(id) return result } # Await the result let promise := fetch_data(42) let data := await promise print("Data: ${data}") # Concurrent execution let p1 := process_file("file1.txt") let p2 := process_file("file2.txt") let p3 := process_file("file3.txt") # Wait for all to complete let r1 := await p1 let r2 := await p2 let r3 := await p3 - Architecture Changes:
- Migrated entire environment handling from Rc<RefCell<>> to Arc<Mutex<>> for Send trait compliance
- Updated Value::Function, Value::AsyncFunction, Value::Generator to use Arc<Mutex>
- All .borrow()/.borrow_mut() calls replaced with .lock().unwrap()
- Proper mutex scope management to prevent deadlocks
- See
examples/async_*.rufffor comprehensive usage examples - See
notes/2026-01-26_async-await-complete.mdfor full implementation details
- Async Functions: Declare functions with
Fixed
- Generator Loop Execution 🔧 - Critical fix for yields inside loop statements:
- Fixed PC (program counter) tracking to support yields inside loops
- Previous implementation advanced PC before statement execution, causing loops with yields to execute only once
- Now PC only advances when statement completes without yielding
- Enables fibonacci(), counter(), and range() generator patterns with loops
- All ROADMAP examples now work correctly
Added
-
Iterators & Iterator Methods 🔄 - Lazy evaluation and functional iteration patterns (P1 feature - COMPLETE):
- Iterator Methods for arrays:
.filter(predicate)- Filter elements based on a predicate function, returns iterator.map(transformer)- Transform each element with a function, returns iterator.take(n)- Limit iteration to first n elements, returns iterator.collect()- Consume iterator and collect into an array
- Method Chaining: Combine multiple iterator operations for data processing pipelines
- Lazy Evaluation: Operations are only executed when
.collect()is called, not when chained - Generator Functions ✅ - Full implementation of
func*andyield:func* name() { yield value }- Generator function syntaxyield expression- Suspend execution and yield a value- Generator instances maintain state between yields (PC, environment)
- Generators work seamlessly with for-in loops
- Infinite generators supported (with manual break)
- Examples:
# Fibonacci generator func* fibonacci() { let a := 0 let b := 1 loop { yield a let temp := a a := b b := temp + b } } # Get first 10 fibonacci numbers count := 0 for n in fibonacci() { print(n) count := count + 1 if count >= 10 { break } } # Simple counter generator func* count_to(max) { let i := 0 loop { if i >= max { break } yield i i := i + 1 } } for num in count_to(5) { print(num) # Prints 0, 1, 2, 3, 4 }
- Syntax:
array.filter(func).map(func).take(n).collect() - Examples:
# Filter even numbers and double them numbers := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] result := numbers .filter(func(n) { return n % 2 == 0 }) .map(func(n) { return n * 2 }) .collect() # Result: [4, 8, 12, 16, 20] # Process scores: filter passing, curve, take top 5 scores := [45, 67, 89, 23, 91, 56, 78, 34, 92, 88] top_curved := scores .filter(func(s) { return s >= 60 }) .map(func(s) { return s + 5 }) .take(5) .collect() - Comprehensive Example:
examples/iterators_comprehensive.ruffwith 8 different usage patterns - Performance: Memory efficient - intermediate arrays not created during chaining, generators don't compute values until requested
- Test Coverage: 6 comprehensive generator tests covering basic yield, state preservation, parameters, early break, and fibonacci sequence
- Iterator Methods for arrays:
-
Crypto Module 🔐 - Advanced encryption and digital signature support (P1 feature):
- AES-256-GCM Symmetric Encryption:
aes_encrypt(plaintext, key)- Encrypt string with AES-256-GCM, returns base64-encoded ciphertextaes_decrypt(ciphertext, key)- Decrypt AES-256-GCM ciphertext, returns plaintext stringaes_encrypt_bytes(data, key)- Encrypt arbitrary bytes with AES-256-GCMaes_decrypt_bytes(ciphertext, key)- Decrypt bytes encrypted with AES-256-GCM- Automatic key derivation via SHA-256 (supports any password length)
- Random nonce generation per encryption (ensures unique ciphertext)
- Authentication tag for integrity verification (GCM mode)
- RSA Asymmetric Encryption:
rsa_generate_keypair(bits)- Generate RSA keypair (2048 or 4096 bits), returns dict with "public" and "private" PEM-encoded keysrsa_encrypt(plaintext, public_key_pem)- Encrypt with RSA public key using OAEP paddingrsa_decrypt(ciphertext, private_key_pem)- Decrypt with RSA private key- Non-deterministic encryption (random padding for each encryption)
- Supports up to 190 bytes plaintext (2048-bit key) or 446 bytes (4096-bit key)
- RSA Digital Signatures:
rsa_sign(message, private_key_pem)- Sign message with RSA private key, returns base64 signaturersa_verify(message, signature, public_key_pem)- Verify signature with RSA public key, returns boolean- PKCS#1 v1.5 signature scheme with SHA-256 hashing
- Provides authentication, integrity, and non-repudiation
- Security Features:
- Industry-standard cryptographic primitives (AES-GCM, RSA-OAEP, PKCS#1 v1.5)
- Proper error handling for invalid keys, corrupted data, and wrong passwords
- PEM format for key storage and exchange
- Hybrid encryption support (combine RSA + AES for large messages)
- Examples:
examples/crypto_aes_example.ruff- File encryption with AES-256-GCMexamples/crypto_rsa_example.ruff- Secure messaging with RSA (Alice-Bob scenario)
- Test Suite:
tests/stdlib_crypto_test.ruffwith 30 comprehensive tests covering encryption, decryption, signing, verification, error handling, and edge cases - Use Cases: Secure file storage, encrypted communications, password protection, data integrity verification, digital signatures, hybrid encryption systems
- AES-256-GCM Symmetric Encryption:
-
Built-in Testing Framework 🧪 - Comprehensive testing support with assertion functions (P2 feature):
- Test Syntax:
test "name" { ... },test_setup { ... },test_teardown { ... },test_group "name" { ... } - Assertion Functions:
assert_equal(actual, expected)- Compare values for equality (Int, Float, Str, Bool, Null, Array, Dict)assert_true(value)- Assert boolean is trueassert_false(value)- Assert boolean is falseassert_contains(collection, item)- Assert array/string/dict contains element
- Test Runner:
ruff test-run <file>command to execute tests with colored output- Runs setup before each test, teardown after each test
- Isolated test environments (fresh interpreter per test)
- Pass/fail/error tracking with timing information
- Verbose mode (
--verbose) for detailed test output
- Test Organization:
- Global setup/teardown for test suite initialization/cleanup
- Test groups for logical organization
- Descriptive test names as strings
- Examples:
examples/testing_demo.ruffwith 27 comprehensive test cases - Test Suite:
tests/testing_framework.ruffwith 25 assertion tests
- Test Syntax:
-
VM Native Function Integration 🚀 - Complete built-in function support in bytecode VM (P1 feature):
- All 180+ Native Functions: VM now has access to every interpreter built-in function
- Zero Code Duplication: VM delegates native function calls to interpreter implementation
- Architecture:
- VM contains Interpreter instance for native function execution
call_native_function_implmethod handles pre-evaluated Value arguments- Automatic registration of all built-in function names via
get_builtin_names()
- Categories Supported:
- Math (abs, sqrt, pow, sin, cos, tan, floor, ceil, round, min, max, etc.)
- String (len, to_upper, to_lower, trim, split, join, replace, etc.)
- Array (push, pop, map, filter, reduce, sort, reverse, etc.)
- Dict (keys, values, ...
0.7.0
Added
-
Range Function 🔢 - Generate number sequences for loops and iteration (P2 feature):
- range(stop) - Generate
[0, 1, 2, ..., stop-1] - range(start, stop) - Generate
[start, start+1, ..., stop-1] - range(start, stop, step) - Generate with custom step size
- Supports negative steps for descending sequences
- Returns empty array for invalid ranges
- Example:
range(5)→[0, 1, 2, 3, 4] - Example:
range(2, 7)→[2, 3, 4, 5, 6] - Example:
range(10, 0, 0 - 2)→[10, 8, 6, 4, 2]
- range(stop) - Generate
-
Format String 📝 - sprintf-style string formatting (P2 feature):
- format(template, ...args) - Format strings with placeholders:
%s- String placeholder (converts any value to string)%d- Integer placeholder (converts numbers to integers)%f- Float placeholder (converts numbers to floats)%%- Escaped percent sign
- Supports variadic arguments
- Returns formatted string
- Example:
format("Hello %s!", "World")→"Hello World!" - Example:
format("%s is %d years old", "Alice", 25)→"Alice is 25 years old" - Example:
format("Success rate: %d%%", 95)→"Success rate: 95%"
- format(template, ...args) - Format strings with placeholders:
-
Extended Math Functions 🧮 - Additional mathematical operations (P2 feature):
- log(x) - Natural logarithm (base e)
- exp(x) - Exponential function (e^x)
- All 13 math functions now available:
abs(),sqrt(),pow(),floor(),ceil(),round(),min(),max(),sin(),cos(),tan(),log(),exp()
-
String Methods 📚 - Comprehensive string manipulation (P2 feature):
- Case conversion:
upper(str)/to_upper(str)- Convert to uppercaselower(str)/to_lower(str)- Convert to lowercasecapitalize(str)- Capitalize first character
- Trimming:
trim(str)- Remove whitespace from both endstrim_start(str)- Remove leading whitespacetrim_end(str)- Remove trailing whitespace
- Character operations:
char_at(str, index)- Get character at indexcount_chars(str)- Count characters (not bytes)
- Validation:
is_empty(str)- Check if string is empty
- Search (existing, now documented):
contains(str, substr)- Check if contains substringstarts_with(str, prefix)- Check prefixends_with(str, suffix)- Check suffixindex_of(str, substr)- Find first occurrence index
- Manipulation (existing, now documented):
replace(str, old, new)/replace_str(str, old, new)- Replace occurrencessplit(str, delimiter)- Split into arrayjoin(array, separator)- Join array into stringsubstring(str, start, end)- Extract substringrepeat(str, count)- Repeat string n times
- Case conversion:
-
Array Mutation Methods 🔧 - Essential array operations (P2 feature):
- push(arr, item) / append(arr, item) - Add item to end
- pop(arr) - Remove and return last item (returns
[modified_array, popped_item]) - insert(arr, index, item) - Insert item at specific index
- remove(arr, item) - Remove first occurrence of item
- remove_at(arr, index) - Remove item at index (returns
[modified_array, removed_item]) - clear(arr) - Return empty array
- Polymorphic functions (work with both strings and arrays):
contains(arr, item)- Check if array contains item (also works with strings)index_of(arr, item)- Find index of item (also works with strings)
- Existing (now documented):
concat(arr1, arr2)- Concatenate two arraysslice(arr, start, end)- Extract sub-array
-
Dict/Map Methods 🗺️ - Essential dictionary operations (P2 feature):
- items(dict) - Get array of
[key, value]pairs - get(dict, key, default?) - Get value with optional default if key not found
- merge(dict1, dict2) - Merge two dictionaries (dict2 overwrites dict1)
- Polymorphic functions:
clear(dict)- Return empty dict (also works with arrays)remove(dict, key)- Remove key-value pair (returns[modified_dict, removed_value], also works with arrays)
- Existing (now documented):
keys(dict)- Get all keys as arrayvalues(dict)- Get all values as arrayhas_key(dict, key)- Check if key exists
- items(dict) - Get array of
-
Assert & Debug Functions 🐛 - Runtime assertions and debug output for testing and troubleshooting (P2 feature):
- assert(condition, message?) - Runtime assertion with optional custom error message:
- Returns
trueif condition is truthy (non-zero numbers, non-null values, true boolean) - Returns error value with message if condition is falsy (0, null, false)
- Falsy values:
false,0(Int),null - Truthy values:
true, non-zero numbers, strings, arrays, objects - Optional message parameter provides context for failed assertions
- Use in guard clauses and input validation
- Example:
assert(x > 0, "x must be positive")→ returnstrueor error
- Returns
- debug(...args) - Print debug output with detailed type information:
- Accepts variadic arguments (any number of values)
- Prints values with full type information for inspection
- Shows detailed structure for arrays, dicts, and nested objects
- Prefixed with
[DEBUG]for easy filtering in logs - Returns
null(useful for debugging without affecting program flow) - Example:
debug("User", user_id, "logged in")→ prints[DEBUG] String("User") Int(123) String("logged in")
- Type Integration: Added type introspection functions (
is_int,is_float,is_string, etc.) to type checker - Variadic Support: Fixed type checker to support variadic functions like
debug() - Comprehensive Testing: 10 new integration tests covering:
- Successful assertions with various data types
- Failed assertions with default and custom messages
- Truthy/falsy value handling
- Assertions in functions and guard clauses
- Debug output for simple and complex values
- Debug with multiple arguments
- Example File:
examples/assert_debug_demo.ruffwith 10 practical use cases - Example:
# Basic assertions assert(age >= 0, "Age cannot be negative") assert(len(items) > 0, "List cannot be empty") # Guard functions func divide(a, b) { if b == 0 { return assert(false, "Division by zero not allowed") } return a / b } # Debug output debug("Processing user:", user_id, "at", timestamp) # Prints: [DEBUG] String("Processing user:") Int(12345) String("at") Int(1706140800) # Debug complex structures data := {"users": [{"name": "Alice", "age": 30}], "count": 1} debug("Data:", data) # Prints: [DEBUG] String("Data:") Dict{users: Array[Dict{name: String("Alice"), age: Int(30)}], count: Int(1)} # Input validation func validate_age(age) { check := assert(age >= 0, "Age cannot be negative") if type(check) == "error" { return check } return "Valid age: " + to_string(age) }
- assert(condition, message?) - Runtime assertion with optional custom error message:
-
Array Utilities 🔢 - Essential array manipulation and analysis functions (P1 feature):
- sort(array) - Sort array in ascending order:
- Works with numbers (Int and Float) and strings
- Returns new sorted array (original unchanged)
- Mixed Int/Float arrays sorted numerically
- Example:
sort([3, 1, 4, 1, 5])→[1, 1, 3, 4, 5]
- reverse(array) - Reverse array order:
- Returns new array with elements in reverse order
- Example:
reverse([1, 2, 3])→[3, 2, 1]
- unique(array) - Remove duplicate elements:
- Preserves order of first occurrence
- Works with any value types
- Example:
unique([1, 2, 1, 3, 2])→[1, 2, 3]
- sum(array) - Calculate sum of numeric elements:
- Returns Int if all elements are Int, Float if any Float present
- Skips non-numeric values
- Empty array returns 0
- Example:
sum([1, 2, 3, 4, 5])→15
- any(array, predicate) - Check if any element satisfies condition:
- Returns
trueif predicate returns truthy for any element - Returns
falsefor empty array - Example:
any([1, 2, 3], func(x) { return x > 2 })→true
- Returns
- all(array, predicate) - Check if all elements satisfy condition:
- Returns
trueif predicate returns truthy for all elements - Returns
truefor empty array (vacuous truth) - Example:
all([1, 2, 3], func(x) { return x > 0 })→true
- Returns
- Comprehensive Testing: 18 new tests covering all functions, edge cases, and chaining
- Example:
scores := [85, 92, 78, 92, 88, 95, 78, 90] # Sort and get unique values unique_scores := sort(unique(scores)) # [78, 85, 88, 90, 92, 95] # Calculate statistics total := sum(scores) # 698 average := total / len(scores) # 87.25 # Check conditions has_excellent := any(scores, func(s) { return s >= 90 }) # true all_passing := all(scores, func(s) { return s >= 60 }) # true # Chain operations top_scores := reverse(sort(unique(scores))) # [95, 92, 90, 88, 85, 78]
- sort(array) - Sort array in ascending order:
-
File Operations 📁 - Essential file manipulation functions for common operations (P1 feature):
- file_size(path) - Get file size in bytes:
- Returns integer byte count (e.g.,
file_size("document.pdf")→1024000) - Useful for checking file sizes before reading or for progress tracking
- Returns integer byte count (e.g.,
- delete_file(path) - Remove a file:
- Deletes the specified file from the filesystem
- Returns
trueon success, error on failure
- rename_file(old_path, new_path) - Rename or move a file:
- Renames file from
old_pathtonew_path - ...
- Renames file from
- file_size(path) - Get file size in bytes:
0.6.0
Added
-
Database Transactions 🎉:
db_begin(db)- Start a database transactiondb_commit(db)- Commit transaction changesdb_rollback(db)- Rollback transaction on errordb_last_insert_id(db)- Get auto-generated ID from last INSERT- Ensures atomic operations across multiple SQL statements
- Full support for SQLite, PostgreSQL, and MySQL
- Automatic transaction cleanup on interpreter shutdown (prevents hangs)
- Example: Money transfers, e-commerce order processing, inventory management
- See
examples/database_transactions.rufffor working examples - See
tests/test_transactions_working.rufffor comprehensive tests
-
Connection Pooling 🎉:
db_pool(db_type, connection_string, config)- Create connection pooldb_pool_acquire(pool)- Acquire connection from pool (blocks if all in use)db_pool_release(pool, conn)- Release connection back to pooldb_pool_stats(pool)- Get pool statistics (available, in_use, total, max)db_pool_close(pool)- Close entire pool and all connections- Configuration options:
min_connections- Minimum pool size (reserved for future use)max_connections- Maximum concurrent connectionsconnection_timeout- Seconds to wait for available connection
- Thread-safe lazy connection creation
- Supports all three database backends: SQLite, PostgreSQL, MySQL
- Critical for production apps with high traffic and concurrent users
- See
examples/database_pooling.rufffor working examples - See
tests/test_connection_pooling.rufffor comprehensive tests
Fixed
- Fixed critical bug where SQLite connections would hang on exit if in active transaction
- Added
Interpreter::cleanup()method to rollback active transactions before drop
Added (Previous)
- Unified Database API:
-
Multi-Backend Support:
- Unified
db_connect(db_type, connection_string)API that works across different databases - Database type parameter:
"sqlite"✅,"postgres"✅,"mysql"(coming soon) - Same
db_execute()anddb_query()functions work with any database backend - Seamless migration path between database types without code changes
- Unified
-
SQLite Support ✅:
db_connect("sqlite", "path/to/database.db")- Connect to SQLite databasedb_execute(db, sql, params)- Execute INSERT, UPDATE, DELETE, CREATE statementsdb_query(db, sql, params)- Query data and return array of dictionaries- Parameter binding with
?placeholders:["Alice", 30] - Full support for NULL values, integers, floats, text, and blobs
-
PostgreSQL Support ✅:
db_connect("postgres", "host=localhost dbname=myapp user=admin password=secret")db_connect("postgresql", ...)- Both "postgres" and "postgresql" accepteddb_execute(db, sql, params)- Execute SQL with$1, $2, $3parameter syntaxdb_query(db, sql, params)- Query with full type support- Parameter binding:
["Alice", 30]mapped to$1, $2in SQL - Supports: SERIAL, INTEGER, BIGINT, REAL, DOUBLE PRECISION, TEXT, BOOLEAN, NULL
- Compatible with PostgreSQL 9.6+ features
-
MySQL Support ✅:
db_connect("mysql", "mysql://user:pass@localhost:3306/myapp")- Asynchronous driver (mysql_async) with transparent blocking interface
- Parameter binding with
?placeholders (MySQL style) - Full CRUD support: CREATE, INSERT, SELECT, UPDATE, DELETE
- Supports: INT, AUTO_INCREMENT, VARCHAR, BOOLEAN, TIMESTAMP, NULL
- Compatible with MySQL 5.7+ and MariaDB 10.2+
-
Common Database Functions:
db_close(db)- Close database connection (works for all database types)- Full parameter binding support prevents SQL injection
- Automatic type conversion between Ruff and database types
- Proper NULL value handling across all databases
-
Transaction Support (Planned):
db_begin(db)- Begin transactiondb_commit(db)- Commit transactiondb_rollback(db)- Rollback transaction- Stub implementations show helpful messages
-
Connection Pooling (Planned):
db_pool(db_type, connection_string, options)- Create connection pool- For high-traffic applications
- Infrastructure designed, implementation planned for future release
-
Use Cases:
- 🍽️ Restaurant menu management (SQLite for local, PostgreSQL for cloud)
- 📝 Blog platforms with PostgreSQL
- 💬 Forums and community sites
- 🛒 E-commerce applications
- 📊 Analytics dashboards
- 🏢 Business management tools
-
Examples:
# SQLite with unified APIdb := db_connect("sqlite", "myapp.db")
db_execute(db, "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)", [])
db_execute(db, "INSERT INTO users (name) VALUES (?)", ["Alice"])
users := db_query(db, "SELECT * FROM users WHERE id > ?", [100])PostgreSQL with same API!
db := db_connect("postgres", "host=localhost dbname=myapp user=admin password=secret")
db_execute(db, "CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT)", [])
db_execute(db, "INSERT INTO users (name) VALUES ($1)", ["Alice"])
users := db_query(db, "SELECT * FROM users WHERE id > $1", [100])MySQL with same API!
db := db_connect("mysql", "mysql://root@localhost:3306/myapp")
db_execute(db, "CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100))", [])
db_execute(db, "INSERT INTO users (name) VALUES (?)", ["Alice"])
users := db_query(db, "SELECT * FROM users WHERE id > ?", [100])Same Ruff code works across all databases!
for user in users {
print(user["name"])
}
db_close(db) -
See
examples/database_unified.rufffor comprehensive SQLite examples -
See
examples/database_postgres.rufffor comprehensive PostgreSQL examples -
See
examples/database_mysql.rufffor comprehensive MySQL examples -
Breaking change: Old
db_connect(path)syntax replaced withdb_connect("sqlite", path) -
Migration: Add
"sqlite"as first argument to existing db_connect() calls
-
Fixed
-
Function Scope Handling:
- Fixed variable shadowing in functions - functions now properly use call-time environment
- Fixed outer variable modification from within functions
- Regular function definitions no longer capture environment (only closures do)
- All 117 tests now pass (fixed 5 previously failing scope tests)
-
Concurrency & Parallelism (v0.6.0):
-
spawn Statement:
spawn { code }- Execute code block in a background thread- Non-blocking execution for fire-and-forget tasks
- Each spawn runs in isolation with its own environment
- Perfect for background processing and long-running operations
-
Parallel HTTP Requests:
parallel_http(urls_array)- Make multiple HTTP GET requests concurrently- Returns array of response dicts in same order as input URLs
- Each response contains
status(number) andbody(string) fields - 3x faster than sequential requests when fetching from 3+ endpoints
- Critical for AI tools comparing multiple model providers (OpenAI, Claude, DeepSeek)
- Ideal for batch processing and data pipelines
-
Channels for Thread Communication:
channel()- Create thread-safe communication channelchan.send(value)- Send value to channel (non-blocking)chan.receive()- Receive value from channel (returns null if empty)- FIFO ordering (first in, first out)
- Perfect for producer-consumer patterns
- Enables coordination between spawned tasks and main thread
-
Use Cases:
- AI Model Comparison: Query GPT-4, Claude, and DeepSeek simultaneously for 3x speedup
- Batch Content Generation: Process 100+ prompts across multiple providers in parallel
- Background Processing: File processing, log analysis, data transformation without blocking
- Web Scraping: Fetch multiple pages concurrently
- API Aggregation: Combine data from multiple services in real-time
-
Examples:
# Parallel HTTP requestsurls := ["https://api1.com", "https://api2.com", "https://api3.com"]
results := parallel_http(urls) # All 3 requests happen simultaneouslyBackground tasks with spawn
spawn {
print("Processing in background...")
process_large_file()
}
print("Main thread continues immediately")Thread communication with channels
chan := channel()
spawn {
result := expensive_computation()
chan.send(result)
}value := chan.receive() # Get result from background thread
-
See
examples/concurrency_parallel_http.rufffor parallel HTTP demo -
See
examples/concurrency_spawn.rufffor spawn examples -
See
examples/concurrency_channels.rufffor channel communication patterns -
See
examples/projects/ai_model_comparison.rufffor real-world AI tool example
-
-
Image Processing (v0.6.0):
- Image Loading:
load_image(path)- Load images from files (JPEG, PNG, WebP, GIF, BMP)- Error handling for missing or invalid image files
- Automatic format detection from f...
- Image Loading:
0.5.0
Added
-
HTTP Server & Networking: Full-featured HTTP client and server capabilities
-
HTTP Client Functions:
http_get(url)- Send GET requests and receive responseshttp_post(url, body)- Send POST requests with JSON bodyhttp_put(url, body)- Send PUT requests with JSON bodyhttp_delete(url)- Send DELETE requests- Returns
Result<dict, string>with status code and response body
-
HTTP Server Creation:
http_server(port)- Create HTTP server on specified portserver.route(method, path, handler)- Register route handlersserver.listen()- Start server and handle incoming requests
-
Request/Response Objects:
http_response(status, body)- Create HTTP response with status code and text bodyjson_response(status, data)- Create HTTP response with JSON body- Request object includes:
method,path,bodyfields
-
Features:
- Method-based routing (GET, POST, PUT, DELETE, etc.)
- Path-based routing with exact matching
- JSON request/response handling
- Automatic request body parsing
- Error handling with proper status codes
-
Example applications:
examples/http_server_simple.ruff- Basic hello world serverexamples/http_rest_api.ruff- Full REST API with in-memory dataexamples/http_client.ruff- HTTP client usage examplesexamples/http_webhook.ruff- Webhook receiver implementation
-
Example:
let server = http_server(8080);server.route("GET", "/hello", func(request) {
return http_response(200, "Hello, World!");
});server.route("POST", "/data", func(request) {
return json_response(200, {"received": request.body});
});server.listen(); // Start serving requests
-
-
SQLite Database Support: Built-in SQLite database functions for persistent data storage
db_connect(path)- Connect to a SQLite database file (creates if not exists)db_execute(db, sql, params)- Execute INSERT, UPDATE, DELETE, CREATE statementsdb_query(db, sql, params)- Execute SELECT queries, returns array of dictsdb_close(db)- Close database connection- Parameters use
?placeholders:db_execute(db, "INSERT INTO t (a, b) VALUES (?, ?)", [val1, val2]) - Query results are arrays of dicts with column names as keys
- Thread-safe with
Arc<Mutex<Connection>>wrapper
-
HTTP redirect_response(): New function for creating HTTP 302 redirect responses
redirect_response(url)- Returns HTTP response with Location header- Used for URL shorteners and OAuth flows
-
Dynamic route path parameters: HTTP server routes now support parameterized paths like
/:code- New
match_route_pattern()function extracts path parameters from URLs - Request object now includes a
paramsdict with extracted path values - Example:
server.route("GET", "/:code", func(request) { code := request.params["code"] }) - Exact routes take priority over parameterized routes (e.g.,
/healthmatches before/:code)
- New
-
Interactive REPL (Read-Eval-Print Loop): Full-featured interactive shell for Ruff
-
Launch with
ruff repl- Start interactive mode for quick experimentation and learning -
Multi-line input support - Automatically detects incomplete statements (unclosed braces, brackets, parentheses)
- Type opening brace
{and continue on next line with....>prompt - Close brace
}to execute the complete statement - Works for functions, loops, conditionals, and any multi-line construct
- Type opening brace
-
Command history - Navigate previous commands with up/down arrow keys
-
Line editing - Full readline support with cursor movement and editing
-
Special commands:
:helpor:h- Display help information:quitor:q- Exit the REPL (or use Ctrl+D):clearor:c- Clear the screen:varsor:v- Show defined variables:resetor:r- Reset environment to clean stateCtrl+C- Interrupt current input
-
Persistent state - Variables and functions remain defined across inputs
-
Pretty-printed output - Colored, formatted display of values
- Numbers:
=> 42 - Strings:
=> "Hello, World" - Booleans:
=> true - Arrays:
=> [1, 2, 3, 4] - Dictionaries:
=> {"name": "Alice", "age": 30} - Functions:
=> <function(x, y)> - Structs:
=> Point { x: 3, y: 4 }
- Numbers:
-
Expression evaluation - Any expression automatically prints its result
-
Error handling - Errors display clearly without crashing the REPL
-
Example session:
ruff> let x := 42ruff> x + 10
=> 52
ruff> func greet(name) {
....> print("Hello, " + name)
....> }
ruff> greet("World")
Hello, World
=> 0
ruff> let nums := [1, 2, 3, 4, 5]
ruff> nums
=> [1, 2, 3, 4, 5]
ruff> :quit
Goodbye! -
See
tests/test_repl_*.txtfor comprehensive examples
-
Changed
- URL Shortener example: Updated to use SQLite database for secure URL storage
- URLs no longer exposed via public
/listJSON endpoint - Stats endpoint now requires POST with code in body
- New
/countendpoint shows total URLs without exposing data - Database file:
urls.dbin working directory
- URLs no longer exposed via public
Fixed
-
Critical: Logical AND (&&) and OR (||) operators not working: The
&&and||operators were completely broken - they always returnedfalseregardless of operands.- Lexer: Added tokenization for
|and&characters to produce||and&&tokens - Parser: Added
parse_or()andparse_and()functions with proper operator precedence (||lowest, then&&, then comparisons) - Interpreter: Added
&&and||cases to the Bool/Bool match arm - Also added
!=operator support for Bool and String comparisons - This fixes URL validation in
url_shortener.ruffwhich usesstarts_with(url, "http://") || starts_with(url, "https://") - Example:
true || falsenow correctly returnstrue(previously returnedfalse)
- Lexer: Added tokenization for
-
URL shortener /list endpoint: Changed from
for code in keys(urls)tofor code in urls- The
keys()function inside closures causes hangs - Direct dict iteration works correctly and iterates over keys
- The
-
Critical: Function cleanup hang bug: Fixed stack overflow that occurred when functions containing loops were cleaned up during program shutdown. Functions can now safely contain loops, nested control structures, and complex logic without hanging.
- Introduced
LeakyFunctionBodywrapper type usingManuallyDropto prevent deep recursion during Rust's automatic drop - Function bodies are intentionally leaked at program shutdown (OS reclaims all memory anyway)
- Updated
url_shortener.ruffexample to use proper random code generation with loops - Added comprehensive tests in
tests/test_function_drop_fix.ruff
- Introduced
-
HTTP functions type checking warnings: Fixed "Undefined function" warnings for HTTP functions in the type checker.
- Registered all HTTP client functions:
http_get,http_post,http_put,http_delete - Registered all HTTP server functions:
http_server,http_response,json_response - HTTP examples now run without type checking warnings
- Added test file
tests/test_http_type_checking.ruff
- Registered all HTTP client functions:
0.4.0
Ruff v0.4.0 - Operator Overloading & Advanced Struct Features
We're excited to announce Ruff v0.4.0, a major release featuring comprehensive operator overloading, explicit self parameters for methods, and enhanced error handling!
🎯 Highlights
✨ Unary Operator Overloading
Structs can now define custom behavior for unary operators:
op_negfor unary minus (-value)op_notfor logical not (!value)
struct Vector {
x: float,
y: float,
fn op_neg(self) {
return Vector { x: -self.x, y: -self.y };
}
}
let v = Vector { x: 3.0, y: 4.0 };
let neg_v = -v; // Vector { x: -3.0, y: -4.0 }
Perfect for:
- Vector and matrix operations
- Complex number arithmetic
- Custom boolean logic
- Flag toggling
🔧 Explicit self Parameter
Methods can now use an explicit self parameter for method composition and builder patterns:
struct Calculator {
base: float,
fn add(self, x) {
return self.base + x;
}
fn chain(self, x) {
return self.add(x) * 2.0; // Call other methods!
}
}
Enable fluent interfaces:
result := Builder { x: 0.0, y: 0.0 }
.set_x(10.0)
.set_y(20.0);
Fully backward compatible - existing code without self continues to work!
⚡ Binary Operator Overloading
Complete support for custom operator behavior:
Arithmetic: op_add (+), op_sub (-), op_mul (*), op_div (/), op_mod (%)
Comparison: op_eq (==), op_ne (!=), op_lt (<), op_gt (>), op_lte (<=), op_gte (>=)
struct Vector {
x: float,
y: float,
fn op_add(self, other) {
return Vector {
x: self.x + other.x,
y: self.y + other.y
};
}
}
v1 := Vector { x: 1.0, y: 2.0 };
v2 := Vector { x: 3.0, y: 4.0 };
v3 := v1 + v2; // Vector { x: 4.0, y: 6.0 }
🛡️ Enhanced Error Handling
Error Properties: Access detailed error information
err.message- Error message stringerr.stack- Call stack trace arrayerr.line- Line number (when available)
Custom Error Types: Define domain-specific errors
struct ValidationError {
field: string,
message: string
}
throw(ValidationError {
field: "email",
message: "Email is required"
})
Error Chaining: Preserve error context
try {
risky_operation()
} except original_err {
throw(DatabaseError {
message: "Failed to process",
cause: original_err.message
})
}
📚 Standard Library Enhancements
Math & Random:
random()- Float between 0.0 and 1.0random_int(min, max)- Random integer in rangerandom_choice(array)- Random array element
Date/Time:
now()- Current timestampformat_date(timestamp, format)- Custom date formattingparse_date(string, format)- Parse date strings
String & Array:
join(array, separator)- Join array elementssplit(string, separator)- Split string into arraytrim(string)- Remove whitespacecontains(haystack, needle)- Search strings/arrays- And many more!
📦 Installation
git clone https://github.com/rufflang/ruff.git
cd ruff
cargo build --release
./target/release/ruff run your_script.ruff📝 Examples
Check out these comprehensive examples:
examples/unary_operators.ruff- Vector negation, complex numbers, boolean flagsexamples/operator_overloading.ruff- Complete operator overloading showcaseexamples/struct_self_methods.ruff- Builder patterns and method compositionexamples/error_handling_enhanced.ruff- Advanced error handling patterns
🔗 Resources
🙏 Acknowledgments
Thank you to everyone who has contributed feedback and ideas for this release!
Full Changelog: https://github.com/rufflang/ruff/blob/main/CHANGELOG.md#040---2026-01-23
0.3.0
Added
- JSON Support: Native JSON parsing and serialization functions
- Doc Comments: Documentation comments for code documentation
- Enhanced Comment Support: All comment types work together seamlessly
- Array Higher-Order Functions: Functional programming operations on arrays for data transformation and processing
- Anonymous Function Expressions: Support for inline function definitions in expression contexts
- Enhanced String Functions: Six new string manipulation functions for common string operations
- String Interpolation: Embed expressions directly in strings with
${}syntax - Parenthesized Expression Grouping: Parser now supports
(expr)for grouping expressions - Loop Control Statements: Full support for
whileloops,break, andcontinue - Modulo Operator: Added
%operator for modulo arithmetic - Not-Equal Operator: Added
!=comparison operator - Boolean Type as First-Class Value: Booleans are now proper runtime values
- File I/O Functions: Complete filesystem operations support
- User Input Functions: Added interactive I/O capabilities
- Lexical Scoping: Implemented proper lexical scoping with environment stack
- Scope Management: Environment now uses Vec scope stack
- Comprehensive Tests: 12 new integration tests for scoping
- Example File:
examples/scoping.ruffdemonstrates all scoping features
Fixed
- Assignment Operator: Fixed
:=to update existing variables instead of always creating new - Function Call Cleanup: Fixed
return_valuenot being cleared after function calls
Changed
- Environment Architecture: Replaced single HashMap with Vec scope stack
0.2.0
Added
- Field Assignment: Full support for mutating struct fields with
:=operator- Direct field mutation:
person.age := 26 - Nested field mutation:
todos[0].done := true - Works with array indexing and dictionary keys
- Direct field mutation:
- Truthy/Falsy Evaluation: If conditions now properly handle boolean values and collections
- Boolean identifiers (
true/false) work in conditionals - Strings: "true" → truthy, "false" → falsy, empty → falsy
- Arrays: empty → falsy, non-empty → truthy
- Dictionaries: empty → falsy, non-empty → truthy
- Boolean identifiers (
- Test Suite: Added 10 comprehensive integration tests covering:
- Field assignment for structs and arrays
- Boolean conditions and truthy values
- Array and dict operations
- String concatenation
- For-in loops
- Variable assignment behavior
- Struct field access
- Example Projects: Two demonstration projects showcasing language features
- Todo Manager: struct mutation, arrays, control flow
- Contact Manager: dictionaries, string functions, error handling
- Clean Build: Zero compiler warnings - all infrastructure code properly annotated
Fixed
- Variable Assignment:
:=operator now consistently creates or updates variables- Previously would fail if variable didn't exist in certain contexts
- Now always inserts/updates in environment
- Boolean Handling: Fixed if statements not recognizing boolean struct fields
- Was only checking numeric values for truthiness
- Now properly evaluates boolean identifiers and other types
- Pattern Matching: Corrected struct pattern matching syntax in field assignment
- Changed from incorrect
Value::Struct(ref mut fields) - To correct
Value::Struct { name: _, fields }
- Changed from incorrect
Changed
- Documentation: Clarified that example projects are demonstrations, not interactive applications
- Build Output: Added
--quietflag recommendation for clean execution output - README: Updated with clearer feature descriptions and usage examples
Known Limitations (Documented)
- No lexical scoping - uses single global environment
- Variable shadowing in blocks doesn't update outer scope (design limitation)
- Booleans stored as string identifiers internally (architectural choice)
- No user input function yet (
input()planned for future release)
Technical Details
- Total tests: 14 (up from 4)
- Compiler warnings: 0 (down from 14)
- Lines of test code added: ~200
- Files modified: interpreter.rs, ast.rs, errors.rs, builtins.rs, module.rs
0.1.0
- First stable release