language
crono-sharp
C#-inspired · statically typed · compiles to crono-vm
.crono
extension
8
passes
C#
syntax
v1
format
A statically-typed, C#-flavored language that compiles to crono-vm bytecode. Classes, interfaces, generics, exceptions, and lightweight concurrency — in a clean multi-pass compiler. Borrows C# syntax while targeting a purpose-built VM.
compiler
pipeline
source → 8 passes → bytecode
input
source (.crono)
pass 1–2
Scanner → Parser → AST
pass 3–4
Symbol Binding → Type Checker
pass 5
Lowering — generators → state machines · foreach expansion
pass 6–7
Symbol Binding + Type Checker (re-run post-lowering)
pass 8
MIR Lowerer → MIR VM Lowerer → bytecode
Passes 3–4 run twice — before and after AST transforms — so generators and foreach expansions are fully type-checked.
modules
imports
multi-file · extension loading
// merge another .crono file into this compilation unit import "math_utils.crono"; import "collections/linked_list.crono"; // load a native C++ extension at runtime extension "crono_websocket"; extension "crono_http";
import
merges .crono file; symbols share scope
extension
loads native C++ module at runtime
resolution
relative path from script file
multiple
supported; circular imports are not
runtime
memory & GC
mark-and-sweep · precise roots · string interning
Primitives (int, long, float, double, bool) are stored by value in VM registers. Objects, strings, and arrays are heap-allocated reference types tracked by the garbage collector.
algorithm
mark-and-sweep
roots
precise — all live references are known
strings
interned — identical literals share one object
value types
int · long · float · double · bool
reference types
string · object · array · dynamic
GC API
GC.GetAllocatedBytes() · GC.GetObjectCount()
compatibility
vs C#
what's in · what's out
in crono-sharp
not implemented
classes + interfaces
get/set properties
generics (monomorphic)
delegates / events
try / catch / finally
LINQ
string interpolation
async/await → orbit instead
nullable T?
operator overloading
var · const · dynamic
pattern matching / tuples
orbit / launch / channels
?. ?? null-aware operators
params variadic
reflection · attributes
example
hello, world
first program · classes · interpolation
class Greeter { public string name; void init(string n) { this.name = n; } string greet() { return "Hello, ${this.name}!"; } } Greeter g = new Greeter(); g.init("crono"); print(g.greet()); // → Hello, crono! var msg = g.greet(); // type inferred const int VER = 1; // immutable
types
primitives
value types · stack-allocated · no boxing
int
32 bit
signed integer
long
64 bit
signed integer
float
32 bit
IEEE 754 single
double
64 bit
IEEE 754 double
bool
true / false
string
ref
immutable, interned
void
no-value return type
dynamic
ref
runtime-typed, no static check
types
arrays
T[] · fixed-size · multidimensional · Array.*
// literal int[] nums = [1, 2, 3]; string[] words = ["a", "b"]; // access int x = nums[0]; nums[1] = 99; int len = Array.Count(nums); // multidimensional int[][] grid = [[1,2],[3,4]]; int v = grid[0][1]; // → 2 // foreach foreach (int n in nums) { print(n); }
Arrays are fixed-size. For resizable sequences use List<T>. Out-of-bounds access throws at runtime.
types
complex types
class · interface · nullable · generic
// class instance Animal a = new Animal(); // interface reference IGreeter g = new Hello(); // nullable T? — may hold nil string? name = nil; if (name != nil) { print(name); } // generic collections List<int> items = List.New<int>(); Dict<string> map = Dict.New<string>(); // dynamic dynamic data = JSON.Parse(json); string val = data["key"];
types
inference
var · const · dynamic · type locking
// var — inferred at declaration, then locked var x = 42; // int var name = "Rex"; // string var items = List.New<int>(); // const — immutable, must initialize const int MAX = 100; const string APP = "crono-sharp"; // dynamic — no static type checking dynamic obj = JSON.Parse(json); dynamic result = obj["status"];
var locks to the inferred type — assigning a different type later is a compile error. dynamic opts out of all static checks.
operators
operators
arithmetic · logic · bitwise · compound
arithmetic
+ - * / %
comparison
== != < <= > >=
logical
&& || !
bitwise
& | ^ ~ << >>
assignment
= += -= *= /= %=
unary
- ! ~ ++ -- (postfix)
string +
concatenates two strings
not in crono#
?. ?? overloading
types
strings
interpolation · verbatim · String.*
// interpolation string s = "${name} v${ver}"; // verbatim (no escape processing) string p = @"C:\Users\file.txt"; // String.* String.Length(s) String.ToUpper(s) String.ToLower(s) String.Substring(s, start, len) String.Contains(s, sub) String.StartsWith(s, prefix) String.IndexOf(s, sub) String.Replace(s, from, to) String.Trim(s) String.Split(s, delim) String.Join(arr, sep) String.Format(template, ...)
control flow
conditionals
if · else if · else
if (x > 0) { print("positive"); } else if (x == 0) { print("zero"); } else { print("negative"); } // condition must be bool bool ok = check(); if (!ok) { return; } // nil check on nullable string? name = getName(); if (name != nil) { print(name); }
No switch/case, no ternary operator. Condition expressions must evaluate to bool — no implicit truthiness.
control flow
loops
for · while · foreach · break · continue
// classic for for (int i = 0; i < 10; i++) { if (i == 5) continue; if (i == 8) break; print(i); } // while while (queue.Count() > 0) { process(queue.Get(0)); queue.Remove(0); } // foreach — arrays and IEnumerable foreach (string item in names) { print(item); } foreach (int n in nums) { total = total + n; }
functions
functions
typed params · return types · void
// typed return int add(int a, int b) { return a + b; } // void void log(string msg) { print("[LOG] " + msg); } // variadic params string fmt(string tpl, params dynamic... args) { return String.Format(tpl, args); } // nullable return string? find(string[] list, string key) { foreach (string s in list) { if (s == key) return s; } return nil; }
functions
generic functions
T inferred at call site · monomorphic
// T inferred from argument T identity<T>(T x) { return x; } int n = identity(42); // T = int string s = identity("hi"); // T = string // generic + collections List<T> wrap<T>(T item) { List<T> l = List.New<T>(); l.Add(item); return l; } // multiple type params Dict<V> pair<V>(string k, V v) { Dict<V> d = Dict.New<V>(); d.Set(k, v); return d; }
Generics are monomorphic — no constraint system (no where T : IFoo). Type is inferred at each call site.
async
generators
yield · state machine · suspend & resume
// yield suspends execution void counter(int max) { int i = 0; while (i < max) { yield; // suspend here print(i); i++; } } // run via orbit — returns Channel<void> Channel<void> ch = orbit counter(5); ch.wait(); // drives to completion
yield
suspends function, saves locals to __state fields
lowering
compiler transforms to state machine class
orbit f()
spawns generator, returns Channel<T>
ch.wait()
blocks until generator finishes
example
flow example
primes · loops · functions · generics
bool isPrime(int n) { if (n < 2) return false; int i = 2; while (i * i <= n) { if (n % i == 0) return false; i++; } return true; } List<int> firstN<T>(int count) { List<int> primes = List.New<int>(); int n = 2; while (primes.Count() < count) { if (isPrime(n)) primes.Add(n); n++; } return primes; } foreach (int p in firstN(10)) { print(p); }
oop
classes
reference types · fields · methods · init
class Animal { public string name; public int age; void init(string n, int a) { this.name = n; this.age = a; } string describe() { return "${this.name} (${this.age})"; } } Animal a = new Animal(); a.init("Rex", 3); print(a.describe()); // → Rex (3)
constructor
method named init() — call manually after new
this
reference to current instance
new
allocates on GC heap
oop
inheritance
single · method override · virtual dispatch
class Dog : Animal { public string sound; void init(string n) { this.name = n; this.age = 0; this.sound = "Woof"; } string describe() { // overrides Animal return "${this.name}: ${this.sound}"; } } Animal a = new Dog(); a.init("Rex"); print(a.describe()); // → Rex: Woof (virtual)
syntax
class Child : Parent
override
same method name and signature
dispatch
virtual by default via vtable
multiple
not supported — single parent only
base call
not supported — call parent method directly
oop
interfaces
method-only contracts · polymorphism · multiple
interface IGreeter { string Greet(); } interface ISpeaker { void Speak(); } // class + interface(s) class Hello : Animal, IGreeter, ISpeaker { string Greet() { return "Hello, ${this.name}!"; } void Speak() { print(this.Greet()); } } // interface reference (polymorphism) IGreeter g = new Hello(); g.init("world"); print(g.Greet());
Interfaces are method-only — no fields. A class may combine one parent and any number of interfaces: class Foo : Bar, IOne, ITwo.
oop
visibility
public · private · space
class Account { private int balance; // class only public string owner; // everywhere space string tag; // same file public void deposit(int amount) { this.balance = this.balance + amount; } private bool canWithdraw(int n) { return this.balance >= n; } }
public
accessible from anywhere
private
accessible within this class only (default)
space
accessible within the same file/module
oop
static
class-level fields · methods · no this
class Counter { private int count; public static int instances; static Counter Create() { Counter c = new Counter(); Counter.instances = Counter.instances + 1; return c; } void increment() { this.count++; // this is valid here } } Counter.Create(); print(Counter.instances); // → 1
static field
shared across all instances
static method
no this — called as ClassName.Method()
access
ClassName.member — never instance.member
example
OOP example
inventory · classes · inheritance · interface
interface IDescribable { string Describe(); } class Item : IDescribable { public string name; public int qty; void init(string n, int q) { this.name = n; this.qty = q; } string Describe() { return "${this.name} ×${this.qty}"; } } class Inventory { private List<Item> items; void init() { this.items = List.New<Item>(); } void add(Item i) { this.items.Add(i); } void print() { foreach (Item i in this.items) { print(i.Describe()); } } }
generics
generics
T syntax · monomorphic · call-site inference
Generics in crono-sharp let functions and collections work across types without losing static type safety. The type parameter T is inferred at each call site from the argument types. No constraint system — T can be any type.
// generic type instantiation List<int> ints = List.New<int>(); List<string> strs = List.New<string>(); Dict<bool> flags = Dict.New<bool>(); Set<Animal> animals = Set.New<Animal>(); // generic function — T inferred T first<T>(List<T> list) { return list.Get(0); } int n = first(ints); // T = int
collections
List<T>
resizable · ordered · generic
List<int> l = List.New<int>(); l.Add(10); l.Add(20); l.Add(30); int count = l.Count(); // → 3 int val = l.Get(1); // → 20 l.Set(1, 99); // [10, 99, 30] l.Remove(0); // [99, 30] // With — returns new list with item appended List<int> l2 = l.With(42); // iterate foreach (int n in l) { print(n); }
New<T>()
create empty list
Add(v)
append to end
Get(i)
element at index (0-based)
Set(i,v)
replace element at index
Remove(i)
remove element at index
Count()
number of elements
collections
Dict<V>
string-keyed · generic value · hash map
Dict<int> scores = Dict.New<int>(); scores.Set("alice", 95); scores.Set("bob", 87); int s = scores.Get("alice"); // → 95 bool has = scores.ContainsKey("bob"); // → true int c = scores.Count(); // → 2 List<string> keys = scores.Keys(); foreach (string k in keys) { print("${k}: ${scores.Get(k)}"); } scores.Remove("bob");
key type
always string
value type
generic V (any type)
Keys()
returns List<string>
collections
Set<T>
unordered · unique elements
Set<string> seen = Set.New<string>(); seen.Add("alpha"); seen.Add("beta"); seen.Add("alpha"); // ignored — already in set int c = seen.Count(); // → 2 bool has = seen.Contains("beta"); // → true seen.Remove("alpha"); List<string> items = seen.ToList();
New<T>()
create empty set
Add(v)
insert; no-op if already present
Contains(v)
membership test
Remove(v)
delete element
ToList()
convert to List<T>
Count()
number of unique elements
collections
IEnumerable
iterator protocol · foreach integration
IEnumerable<T> is the iteration contract. Any class implementing it can be used in foreach. List<T> and Set<T> implement it; arrays support foreach natively.
// custom iterable class class Range : IEnumerable<int> { public int from, to; void init(int f, int t) { this.from = f; this.to = t; } int Current() { ... } bool MoveNext() { ... } void Reset() { ... } } Range r = new Range(); r.init(1, 5); foreach (int n in r) { print(n); }
example
word count
Dict · List · string ops
string[] words = String.Split(text, " "); Dict<int> freq = Dict.New<int>(); foreach (string w in words) { string key = String.ToLower(w); if (freq.ContainsKey(key)) { freq.Set(key, freq.Get(key) + 1); } else { freq.Set(key, 1); } } List<string> keys = freq.Keys(); foreach (string k in keys) { print("${k}: ${freq.Get(k)}"); }
runtime
exceptions
try · catch · finally · throw
try { int result = riskyOp(); if (result < 0) { throw new Exception("negative"); } } catch (Exception e) { print("Error: " + e.message); } finally { cleanup(); // always runs } // re-throw try { doWork(); } catch (Exception e) { log(e.message); throw e; }
throw
throws any Exception instance
catch
catches Exception base class only — no typed hierarchy
e.message
string message on the exception
finally
always executes, success or exception
propagation
unhandled exceptions bubble up the call stack
stdlib
String.*
immutable operations · full API
method
signature
returns
Length
String.Length(s)
int
ToUpper
String.ToUpper(s)
string
ToLower
String.ToLower(s)
string
Substring
String.Substring(s, start, len)
string
Contains
String.Contains(s, sub)
bool
StartsWith
String.StartsWith(s, prefix)
bool
IndexOf
String.IndexOf(s, sub)
int
Replace
String.Replace(s, from, to)
string
Trim
String.Trim(s)
string
Split
String.Split(s, delim)
string[]
Join
String.Join(arr, sep)
string
Format
String.Format(tpl, ...)
string
stdlib
Math.*
arithmetic · trig · log · constants
method
notes
Abs · Min · Max
basic numeric
Sqrt · Pow
Pow(base, exp)
Round · Floor · Ceil
rounding
Sin · Cos · Tan
radians
Asin · Acos · Atan
inverse trig
Atan2(y, x)
full-quadrant angle
Log · Log10 · Exp
logarithm / exp
PI · E
constants (double)
stdlib
IO.*
files · directories · paths
// files string text = IO.ReadFile("data.txt"); IO.WriteFile("out.txt", text); IO.AppendFile("log.txt", "entry\n"); bool ok = IO.FileExists("data.txt"); IO.DeleteFile("tmp.txt"); // directories List<string> files = IO.ListDirectory("./"); IO.CreateDirectory("output/"); bool d = IO.DirectoryExists("output/"); // paths string full = Path.Combine("a", "b.txt"); string dir = Path.GetDirectoryName(full);
stdlib
Console · System · Time
I/O · environment · timing
// Console Console.Print("hello"); string line = Console.ReadLine(); string all = Console.ReadAll(); string? ln = Console.ReadLineTimeout(500); // System string[] args = System.Args(); string home = System.GetEnv("HOME"); string path = System.ScriptPath(); System.Execute("ls -la"); System.Exit(0); // Time double now = Time.Now(); Time.Sleep(100); // milliseconds long ms = Time.GetTicksMs(); string ts = Time.Format(now, "%Y-%m-%d");
concurrency
concurrency
orbit · launch · Channel<T>
// orbit — coroutine, returns Channel<T> int compute(int n) { Time.Sleep(10); return n * n; } Channel<int> ch = orbit compute(5); int result = ch.wait(); // → 25 // launch — fire-and-forget thread void worker() { print("background"); } launch worker(); // channel messaging Channel<string> pipe = Channel.New<string>(); pipe.send("hello"); string msg = pipe.wait();
orbit f()
spawns coroutine, returns Channel<T>
launch f()
spawns thread, no return value
ch.wait()
blocks until value is ready
ch.send(v)
delivers value to channel
stdlib
JSON
parse · serialize · path navigation
// parse JSON string → dynamic dynamic obj = JSON.Parse('{"name":"crono","v":1}'); string name = obj["name"]; // → "crono" int v = obj["v"]; // → 1 // serialize Dict → JSON string Dict<string> d = Dict.New<string>(); d.Set("status", "ok"); string json = JSON.Serialize(d); // deep path navigation dynamic data = JSON.Parse(nested); dynamic city = JSON.Path(data, "user.address.city");
JSON.Parse
string → dynamic (Dict-like)
JSON.Serialize
object/Dict → JSON string
JSON.Path
dot-path navigation on parsed JSON
stdlib
Encoding
Base64 · Hex · HMAC-SHA256
// Base64 string b64 = Encoding.Base64Encode(data); string raw = Encoding.Base64Decode(b64); // Hex string hex = Encoding.HexEncode(bytes); // HMAC-SHA256 string mac = Encoding.HmacSha256(key, message);
Base64Encode
bytes/string → base64 string
Base64Decode
base64 string → original bytes
HexEncode
bytes → lowercase hex string
HmacSha256
key + message → MAC string
stdlib
Random · Hash
pseudo-random · seeding · hashing
// Random int n = Random.NextInt(0, 100); double d = Random.NextDouble(); Random.Seed(42); // deterministic // Hash int h = Hash.Fnv1a("hello");
Random.NextInt(lo, hi)
int in [lo, hi)
Random.NextDouble()
double in [0.0, 1.0)
Random.Seed(n)
set deterministic seed
Hash.Fnv1a(s)
FNV-1a hash of string → int
testing
Assert
test assertions · compile-time checks
// assertions (throw on failure) Assert.True(x == 1, "x must be 1"); Assert.False(list.Count() == 0, "not empty"); Assert.Fail("should not reach here"); // typical test pattern void testAdd() { int result = add(2, 3); Assert.True(result == 5, "2+3 should be 5"); } // test runner (external C++ harness) // ASSERT_STDOUT(source, expected) // ASSERT_THROWS(source) // ASSERT_EQ(source, expected)
Assert methods throw an Exception with the provided message on failure. The external test harness catches and reports them.
runtime
GC API
introspection · allocation · limits
// inspect heap int bytes = GC.GetAllocatedBytes(); int objs = GC.GetObjectCount(); print("heap: ${bytes} bytes, ${objs} objects"); // typical use: leak detection in tests int before = GC.GetObjectCount(); doWork(); int after = GC.GetObjectCount(); Assert.True(after == before, "no leak");
GetAllocatedBytes()
total heap bytes currently allocated
GetObjectCount()
number of live GC-managed objects
exec limit
CronoConfig::maxOpcodes → StepResult::ERROR
stdlib
Buffer
raw bytes · binary I/O · graphics
Buffer is a mutable byte array for low-level binary data. Used for network payloads, graphics buffers, and binary file I/O.
// create and fill Buffer buf = Buffer.New(1024); int len = buf.Count(); // byte-level access (via offset) // typically used with extensions: Window.SetPixel(buf, x, y, r, g, b); // write to file IO.WriteFile("output.bin", buf);
Buffer.New(size)
allocate buffer of given byte size
buf.Count()
size in bytes
use case
graphics · binary I/O · native extension interop
extensions
extensions
native C++ modules · type-safe at compile time
// declare extension — loads at runtime extension "crono_websocket"; extension "crono_http"; // use the extension API int conn = WS.Connect("wss://api.example.com", "{}"); WS.Send(conn, '{"action":"ping"}'); string msg = WS.Receive(conn, 5000); WS.Close(conn);
declaration
extension "name"; — relative or absolute path
type checking
compile-time — signatures declared in C++ manifest
loading
dynamic link at runtime via dlopen/LoadLibrary
NativeFunction
{"name","returnType",{paramTypes},fnPointer}
extensions
WebSocket · HTTP
crono_websocket · crono_http
// WebSocket extension "crono_websocket"; int conn = WS.Connect(url, opts); WS.Send(conn, payload); string msg = WS.Receive(conn, timeoutMs); bool more = WS.Poll(conn); WS.Close(conn); // HTTP extension "crono_http"; Dict<string> headers = Dict.New<string>(); headers.Set("Authorization", "Bearer ${token}"); string body = HTTP.Get(url, headers); string res = HTTP.Post(url, payload, headers);
extensions
Process · Window · JS
crono_process · crono_window · crono_js
// Process extension "crono_process"; Process.Run("ls -la"); // Window (graphics) extension "crono_window"; Window.Open(800, 600, "Demo"); while (!Window.IsClosed()) { Buffer buf = Buffer.New(800 * 600 * 4); // ... draw pixels into buf ... Window.Update(buf); } Window.Close(); // JS embedding extension "crono_js"; JS.Eval("console.log('hello from JS')");
interop
dynamic FFI
Dynamic.Load · runtime DLL binding · untyped
// load any shared library at runtime dynamic lib = Dynamic.Load("ucrtbase.dll"); // call C functions by name double s = lib.sin(1.5707963); // → 1.0 double p = lib.pow(2.0, 10.0); // → 1024.0 // any cdecl/stdcall export works dynamic mylib = Dynamic.Load("mylib.so"); int result = mylib.my_function(42);
Dynamic.Load
loads shared lib, returns dynamic handle
resolution
symbol lookup at call time — no compile check
arg types
int · long · float · double · string
vs extension
extension: typed + compile-checked · FFI: runtime only
interop
C API
embed crono-vm · native function registration
// create and configure VM CronoVM* vm = crono_vm_create(&config); crono_vm_load(vm, bytecode, length); // register native function NativeFunction fn = { "Math.Sin", "double", {"double"}, [](Memory* mem, ...) { /* impl */ } }; crono_vm_register_native(vm, &fn); // run StepResult r = crono_vm_run(vm); crono_vm_destroy(vm);
crono_vm_create
allocate VM with CronoConfig
crono_vm_load
load compiled bytecode
crono_vm_run
execute until halt or error
crono_vm_step
single-step execution
crono_vm_destroy
teardown + GC flush
interop
C# interop
CronoVM.cs · call crono from .NET
A C# wrapper at interop/csharp/ exposes the crono-vm C API to .NET applications. Allows compiling and running crono-sharp programs from C# code — useful for embedding crono-sharp as a scripting layer in a .NET host.
CronoVM.cs
P/Invoke wrapper over the C API
InteropTests.cs
test suite for the C# binding
use case
embed crono-sharp as a scripting engine in a .NET app
status
basic / legacy — not the primary integration path
For most embedding use cases, the C API (fig. 7.4) is the primary path. The C# wrapper is a thin convenience layer over it.