This is a small compiler prototype for a C#-inspired systems language with Rust-like ownership checks.
Accepted source file extensions are .gl and .cs. The parser is the same for both; .cs is intended for C#-migration snippets.
The repo now exposes a gl CLI surface alongside the lower-level glitchc compiler entrypoint. Use:
cargo run --bin gl -- helpCurrent gl commands:
gl new console|classlib|xunit [name]gl restore [project|solution|directory|file]gl build [project|solution|directory|file] [--output <path>]gl publish [project|solution|directory|file] [--output <path>]gl run [project|directory|file] [-- <program args...>]gl test [project|solution|directory|file]gl clean [project|solution|directory|file]gl sln new|add|remove|listgl store [project|solution|directory|file] [--output <path>] [--package-id <id>] [--package-version <version>]gl watch [--poll-ms <ms>] [--once] <build|publish|run|test|clean|restore|format> [...]gl format [project|solution|directory|file] [--check]
Current command limitations:
gl restoreis source-linked only. There is no remote feed restore yet.gl watchis a polling watcher over project/source files, not an OS-native file notification backend.gl formatcurrently applies a conservative whitespace/indentation formatter for.gl/.cssources, not a full Roslyn-equivalent formatter.gl slnmanages Glitching solution manifests in.slnfiles for thegltoolchain; it is not full MSBuild/Visual Studio solution parity..csprojmetadata is now honored for the current native toolchain slice:AssemblyNamedrives artifact/package namesOutputTypedistinguishes runnable executables from library-style builds; library projects build to LLVM bitcode (.bc) by defaultTargetFrameworkis validated as a Glitching target marker (gl*)ProjectReferenceparticipates in source loadingPackageReferenceis carried intogl restore,gl test, andgl storepackage metadataCompile Include/Removecontrols project source membershipContent Include/None Includeare copied bygl publishand packed bygl storeIsTestProjectcontrols whethergl testwill execute the project
RootNamespacewraps project source files that do not already declarenamespaceorpackage, andStartupObjectselects the emitted native entrypoint from a staticMain/mainmethod on the configured type.
The language frontend is implemented directly in Rust compiler code under src/lexer.rs and src/parser.rs. There is no package-backed System.CSharp lexer/parser surface in the current compiler.
Current implemented subset:
fn name() { ... }- C#-style free functions with typed parameters and return values, e.g.
int Add(int a, int b) { return a + b; } - top-level C#-style
using System;andusing Alias = System.Name;directives let,let mut, and C#-stylevar- C#-style
intandlonglocal declarations stringlocal declarations and string literals- integer literals
- fixed-size arrays with
int[] xs = { 1, 2, 3 }; new int[] { 1, 2, 3 }- array literals with
[1, 2, 3] - array indexing with
xs[0] structdeclarations with fieldsref structdeclarations withref int/ref longfieldsclassdeclarations lowered to owned heap allocations- object initializers with
new Type { Field = value } - field access with
value.Field List<int>withnew List<int>(),.Add(value), and indexingList<T>.GetEnumerator()on the supported list surface, plusDictionary<K,V>.GetEnumerator()/ dictionaryforeachlowering on the current LLVM snapshot-enumerator pathDictionary<string, int>withnew Dictionary<string, int>(),.Add(key, value), and indexing- package-backed user-defined indexer lowering for
target[index]when the target exposesget_Item(...), including the currentIStringLocalizer<T>compatibility slice HashSet<string>withnew HashSet<string>(),.Add(value),.Contains(value), and.Clear()System.Collections.Generic.List<int>andSystem.Collections.Generic.Dictionary<string, int>, includingusingaliases- C#-style
Threadwithnew Thread(worker),.Start(), and.Join() System.Threading.Tasks.TaskwithTask.Run(worker),task.Wait(), andtask.GetAwaiter()System.Threading.Tasks.Task<T>forint,long, and ownedstringresults withTask.Run(worker),task.Result,task.GetResult(), andtask.GetAwaiter()System.Threading.Tasks.Task.IsCompleted,Task.WhenAll(Task[]),ValueTask<T>.AsTask(), and the matchingValueTask/ValueTask<T>awaiter helpersSystem.Threading.Tasks.Task.WhenAll(Task<T>[])for the supported task-array subsetasync Task,async Task<T>,async ValueTask, andasync ValueTask<T>lowered to compiler-generated worker-thread state-machine entrypoints with hidden resume functions- blocking
awaitinside the supported async gate for local declarations, assignments,if/else,return,switch,try/catch/finally, and the currently exercised loop shapes - ASP.NET-style
MapGet/MapPostdelegate handlers in the current zero-argument slice, including LLVM lowering forTask<string>/Task<class>route handlers WebApplicationBuilder.Build()now carries the builtServiceProviderinto the returnedWebApplication, so builder-style controller routes can resolve the current named-string DI slice without a manualapp.Services = ...patchSystem.Linq.Enumerablenow has workingIEnumerable<T>overloads forCount,Any,First,FirstOrDefault,ToList, andToArraySystem.Array.Empty<T>()lowers through a native helper on the current core-library surface, andbool.Parse/ thestringnull helpers compile on the current pathstring.ToLower(),string.ToLowerInvariant(),string.Replace(...),string.Trim(),string.Contains(...),string.StartsWith(...),string.EndsWith(...),string.Substring(...),string.TrimStart(...), andstring.TrimEnd(...)now lower through native runtime helpers or direct LLVM lowering on the LLVM pathint.Parse,int.TryParse,DateTime.Parse, andTimeSpan.FromMinutesare present on the currentSystemsurfaceSystem.IO.Path.GetExtensionandSystem.IO.Path.GetFileNameare available on the current file-system surfaceSystem.Text.Encoding.UTF8.GetBytesis available on the current text-encoding surfaceSystem.Typenow carries package-backed reflection metadata forGetMethod,GetProperty,GetProperties,GetGenericArguments, andGetGenericTypeDefinition, which is enough for the current package reflection slice and open generic checks such asICollection<>- compiler intrinsics such as
sizeof(T)in the LLVM backend - a concrete
Rc<int>LLVM layout and drop glue for the current ownership test slice - generated-regex partial methods for the slug helper pattern used by the ASP.NET sample
- variables
- mutable assignment, e.g.
x = x + 1; if/elsewith scalar comparison conditionswhileloops- C#-style
forloops lowered throughwhile move xwith drop ownership transfer for moved valuesborrow xborrow mut x- block scopes with scoped borrows and scoped drops
return;andreturn expr;with scoped cleanup before function exit- recursive drops for owned fields inside user-defined
structandclassvalues - owned
stringvalues with automatic cleanup - owned values such as
string,List<int>, classes, and structs containing owned fields requiremoveinstead of implicit copies Task<string>.Resulttransfers owned string results to the caller; task cleanup will not free a moved-out resultprint(expr)+on owned integers- scalar comparisons:
==,!=,<,<=,>,>= - typed IR to LLVM IR generation
- LLVM bitcode and native executable generation through Clang
- Rust static runtime linkage for the LLVM HTTP socket host
- reference-counted dynamic LLVM strings with deterministic release
- typed
try/catch/finallyexception propagation in LLVM - a small ASP.NET-style helper surface, including
ModelState.AddModelError,ControllerBase.Ok, andControllerBase.NotFound - simple configuration and model-builder support for the current ASP.NET / EF compatibility slice, including environment-backed
ConfigurationManager["key"],GetSection(...),GetValue<string|int|long|bool>(...), andGetConnectionString(...) - default CLI output builds a native executable when no explicit output is requested
- NuGet package emission produces LLVM-native assets and linked source metadata
- source-level packages with
package Name;andnative "C source"; - source directories and
.csprojfiles can be used as compiler inputs;.csprojsupport now honors the current property/item slice above, but it is still not full MSBuild parity - service-collection / builder configuration delegates such as
AddLocalization(...),AddCors(...),AddMvc(...).AddJsonOptions(...),AddAuthentication(),AddJwtBearer(...), andAddSwaggerGen(...)now execute through the native LLVM path for the current expression-bodied / assignment-bodied lambda slice
var x = value; is accepted as a conversion-friendly spelling for let mut x = value;.
Nullable reference syntax is parsed. Nullable value types (T? on structs/scalars), lifted conversions, and value-type boxing/unboxing are implemented for the current supported subset, but broader .NET parity still needs more runtime and framework work.
Current async boundary:
- supported awaitables in native lowering are
Task,Task<T>,ValueTask, andValueTask<T> awaitis lowered through a blocking worker-thread runtime, not a non-blocking scheduler- owned/shared/copy values may cross
await; borrowed/view values that stay live across suspension are rejected with rewrite guidance - suspension inside loops is still limited to the currently exercised shapes, and the gate remains intentionally blocking over worker threads rather than event-loop based
- async route handlers now compile and run through typed endpoint thunks on the native host path for the current
Task<string>/Task<class>slice - generic
UseMiddleware<T>()and several ASP.NET/DI registration markers such asAddEndpointsApiExplorer(),AddMemoryCache(), plus direct host/logging/Swagger compatibility markers still emit explicitGL3013warnings instead of silently looking implemented - async socket/event-loop hosting, cancellation,
ConfigureAwait, andSynchronizationContextsemantics are not implemented yet
Owned local returns now lower as moves on the LLVM path. Returning a local string, class, or other owned value no longer injects an extra retain before the callee drops its locals, which closes a leak edge in package/helper methods that return owned temporaries.
Reference cycles over owned graphs are diagnosed with GL3007 and rewrite guidance such as Weak<T>. The compiler can detect the cycle and point to the source field, but arbitrary cyclic ownership is not automatically leak-free in the current memory-safe model.
Supported C#-compatibility examples:
using System;
using System.Collections.Generic;
int x = 10;
long y = x + 20;
int[] scores = { 7, 8, 9 };
var moreScores = new int[] { 10, 11, 12 };
print(scores[0]);Larger supported example:
struct Point {
int X;
int Y;
}
class Player {
public string Name;
public int Hp;
}
ref struct IntView {
public ref int Data;
public int Length;
}
fn main() {
string name = "Ada";
Point p = new Point { X = 2, Y = 3 };
Player player = new Player { Name = "Ada", Hp = 100 };
int x = 42;
IntView view = new IntView { Data = borrow mut x, Length = 1 };
List<int> xs = new List<int>();
xs.Add(10);
Dictionary<string, int> scores = new Dictionary<string, int>();
scores.Add("hp", 100);
print(name);
print(p.X);
print(player.Hp);
print(view.Data);
print(xs[0]);
print(scores["hp"]);
}Threading subset:
fn worker() {
print(42);
}
fn main() {
Thread t = new Thread(worker);
t.Start();
t.Join();
}System.Collections.Generic subset:
using System.Collections.Generic;
using Collections = System.Collections.Generic;
fn main() {
System.Collections.Generic.List<int> xs = new System.Collections.Generic.List<int>();
xs.Add(10);
Collections.Dictionary<string, int> scores = new Collections.Dictionary<string, int>();
scores.Add("hp", 100);
}System.Threading.Tasks subset:
using System.Threading.Tasks;
fn worker() {
print(42);
}
fn main() {
Task task = Task.Run(worker);
task.Wait();
}Run:
cargo run -- examples\ok.gl
cargo run -- examples\csharp_data_structures.gl
cargo run -- examples\ref_struct.gl
cargo run -- examples\thread.gl
cargo run -- examples\old_csharp.cs
cargo run -- examples\system_collections_generic.cs
cargo run -- examples\system_threading_tasks.cs
cargo run -- examples\scopes.glVS Code syntax highlighting:
- a lightweight VS Code extension now lives at editor/vscode/glitching-language-support
- it registers
.glas a Glitching language and reuses the Microsoft C# extension grammar where possible - it adds Glitching-specific tokens like
fn,let,move,borrow,package,native,shared<T>, andXUNIT_FACT(...)
Emit LLVM IR:
cargo run -- examples\llvm_simple.gl --emit-llvm-ir out.llEmit LLVM bitcode:
cargo run -- examples\llvm_simple.gl --emit-llvm-bc out.bcBuild a native executable directly from LLVM IR:
cargo run -- examples\llvm_simple.gl --emit-exe out.exe
.\out.exeTesting strategy:
- most language/runtime semantics should be exercised in
.gl/.cssource fixtures, especially xUnit-style suites under examples/xunit_sorting and examples/xunit_runtime_surface - Rust-side tests should stay focused on compiler/product acceptance gates such as
.csprojloading, package linking, LLVM/native emission, diagnostics, and native process execution - today the sorting fixture is the native xUnit execution smoke path; the broader runtime-surface fixture is kept as a source-level LLVM compile gate while task/runtime gaps are still being closed
LLVM-only commands use the native LLVM toolchain. On Windows, the compiler searches
PATH, GLITCH_LLVM_BIN, and C:\Program Files\LLVM\bin. It also discovers installed
Visual Studio 2022 MSVC and Windows SDK libraries for native linking.
Run the native ASP.NET-like ownership load test:
.\scripts\measure-aspnet-load.ps1The test builds examples/aspnet_load_test.cs, performs 2,000,000 in-process routed
requests per run, and samples peak process memory. LLVM-generated allocations use counted
allocation wrappers. A native main exits with code 1 when tracked allocations remain
after deterministic cleanup and 0 when the tracked count returns to zero.
Build the one-request Rust HTTP host smoke test:
cargo build
cargo run -- examples\aspnet_socket_smoke.gl --emit-exe target\aspnet-socket-smoke.exeIt serves GET /health on port 5099, returns {"status":"ok"}, then exits. Set
GLITCH_REPORT_LEAKS=1 to print the remaining tracked allocation count at process exit.
Emit a NuGet package:
cargo run -- examples\old_csharp.cs --emit-nuget Glitching.Sample.0.1.0.nupkg --package-id Glitching.Sample --package-version 0.1.0Build the standalone System.Collections.Generic package from Glitching-lang source:
cargo run -- stdlib\System.Collections.Generic.gl --emit-nuget System.Collections.Generic.0.1.0.nupkg --package-version 0.1.0Build the standalone System.Threading.Tasks package from Glitching-lang source:
cargo run -- stdlib\System.Threading.Tasks.gl --emit-nuget System.Threading.Tasks.0.1.0.nupkg --package-version 0.1.0Package emission writes LLVM IR plus linked source metadata into the .nupkg.
Expected rejected examples:
cargo run -- examples\use_after_move.gl
cargo run -- examples\borrow_conflict.glThis is intentionally a C#-compatibility subset, not full C#. The typed LLVM backend now
supports user class layouts, constructors, instance methods, fields, reference-counted class
ownership, and owned List<T> / Dictionary<K,V> buffers for the currently lowered scalar,
string, and pointer element types. It also has concrete LLVM lowering for sizeof(T) and the
current Rc<int> ownership/layout path used by the memory-leak tests. It does not use a tracing GC.
Important remaining safety boundaries:
- possible reference cycles produce warnings with
Weak<T>rewrite proposals, but unchanged source can still accept cycle-leak risk; - arrays, lists, and dictionaries now release nested owned collection elements in the current recursive-drop slice, but broader arbitrary shared graph ownership is still not automatically leak-free;
- dynamic LLVM strings use deterministic reference counting, and the allocation registry is now synchronized for the current concurrent-runtime slice;
- the socket host is implemented in the linked Rust runtime and is currently a synchronous HTTP/1.1 loop;
- C-only
nativepackage blocks remain legacy-backend-only; - the full async scheduler, executable delegate middleware, generated controller routing, JSON object serialization, and EF query execution are still incomplete;
- the allocation counter covers LLVM allocations routed through
glitch_calloc,glitch_realloc, andglitch_free; it is not a replacement for an external native memory sanitizer.
Unsupported syntax and runtime behavior are rejected explicitly:
unsafeblocks, pointer types, pointer arithmetic, pointer comparisons, and pointer-member accessfixedstatements and fixed-size buffers- raw-pointer
stackalloc - finalizers that depend on GC reachability
- pinning and pin-sensitive interop patterns
- exact weak-reference / object-header semantics from .NET
When the compiler hits one of these cases, it should emit a rewrite suggestion instead of a silent fallback:
- replace pointer code with owned arrays,
ref structviews, or a narrow native helper - replace
fixedwith a scoped copy or aref structview that does not escape - replace raw-pointer
stackallocwith a bounded owned buffer or a native helper - replace finalizers with
Dispose/using - replace pinning assumptions with copy-based data flow
- replace exact weak-reference behavior with
Weak<T>-style compatibility helpers or accept that exact .NET behavior is unavailable
The cloned Backend/Library source tree currently parses, lowers, and links to a native
executable in the supported subset. Compatibility diagnostics still identify unresolved ASP.NET
Core, third-party, controller, and EF Core behavior; successful linking does not yet mean that
application routes or database operations are functionally equivalent to .NET.