Hey everyone,
I know it’s been a while since I’ve posted anything meaningful but I wanted to share a project that I’ve been working on since the xmas holidays.
I was bored apparently. I decided to learn how compilers work and how to write my own language and frontend for LLVM. The result of this journey is giving me a lot more respect for the folks at my old job and those that came before me, if just in the sheer complexity of it all.
Now I did use a lot of what I learned between 2011 and 2022, but I am also fixing a lot of the things that were just wrong IMHO. I’m adding language features that should and could have existed a long time ago.
If you’re curious… I’m calling it Buoy. If anyone is interested, I’ll put together a package for people to look at. Currently it can only compile on macOS, but Windows and Linux are coming soon.
Buoy vs X: Language Differences
This guide is for developers coming from X. Buoy was designed to feel familiar: the same style syntax, the same native-UI philosophy, and the same Automatic Reference Counting memory model. Most code you write in Buoy will look like code you have already written in X.
But Buoy adds a command-line compiler with full cross-compilation, a generics system, several new language features, and a different toolchain philosophy. This document calls out the differences so you know what’s new, what’s changed, and what isn’t there yet.
Toolchain & Compilation
Command-line compiler
Buoy is always command-line first:
buoy hello.bui -o hello
buoy hello.bui --target arm64-linux -o hello
buoy hello.bui --target x86_64-windows -o hello.exe
There is no GUI build step.
Cross-compilation without target SDKs
From a single macOS host you can compile for all six supported targets:
| Target | --target value |
|---|---|
| macOS arm64 | arm64-darwin |
| macOS x86_64 | x86_64-darwin |
| Linux arm64 | arm64-linux |
| Linux x86_64 | x86_64-linux |
| Windows arm64 | arm64-windows |
| Windows x86_64 | x86_64-windows |
None of these targets require the platform SDK installed on the host. Buoy ships pre-built per-target runtimes and SDK stubs. You can cross-compile to all six from one machine.
Distributable compiler binary
The buoy binary vendors LLVM and lld as static libraries. It has no dependency on Homebrew or any system package manager after installation. You can put it on a CI server, copy it to another machine, and it just works.
Declarative UI vs visual designer
X uses a drag-and-drop visual designer embedded in the IDE. Buoy uses textual Window … End Window blocks instead.
Window MyWindow
Title = "Example"
Frame = (100, 100, 400, 300)
Label lblHello
Text = "Hello, world!"
Frame = (20, 20, 360, 30)
End Label
End Window
These blocks lower to a Window subclass at compile time. A round-trip visual layout editor is under construction but not yet shipped. The VSCode extension provides syntax highlighting, go-to-definition, and diagnostics via the Buoy language server.
Optimization
Buoy exposes LLVM optimization levels directly:
buoy app.bui -O2 -o app
buoy app.bui -Os -o app ' optimize for size
Syntax Familiar from X
These work exactly as you expect:
Dim x As Integer/Var x As Integer(Varis an exact synonym forDim)Const Pi As Double = 3.14159If … Then … Else … End IfFor i As Integer = 0 To 9 … NextFor Each item In collection … NextWhile … Wend/Do … LoopSelect Case … End SelectFunction,Sub,ReturnClass,Interface,ModuleInherits,OverridesMe,Super,NewImplementsTry … Catch … Finally … End Try/RaiseDelegate Function,AddressOfEvent,Handle,RaiseEventProperty … Get … Set … End PropertySharedfields and methodsTrue,False,Nil- Single-quote
'line comments
Similar but new
Importfor modulesAbstract,FinalAssertAsync,Await,TaskAtomicWithandEnd WithBlockLiterals (inline functions) on MacOS- Compound assignments +=, -=, *=, /=
- Mutex Conditions
#Define,#Undef- Generics
- Multi-line comments
- Exported functions
- Lazy properties
- Optional and Result
- Optional chain
New Language Features
Generics
Buoy has a full generics system. Classes, interfaces, free functions, and instance methods can all be parameterized over types:
Class Box Of T
Dim Value As T
End Class
Dim b As New Box Of Integer
b.Value = 42
Function Identity Of T(x As T) As T
Return x
End Function
Dim n As Integer = Identity(42) ' infers Of Integer at the call site
Generics are monomorphized at compile time: the compiler produces a concrete copy of the class or function for each distinct type argument. There is no runtime erasure and no boxing overhead.
Optional chaining (?.)
The ?. operator guards an assignment against a Nil receiver. If the receiver is Nil, the assignment is silently skipped:
Dim w As Window = GetWindowMaybe()
w?.Title = "Updated" ' no-op if w is Nil
This is parse-time sugar for:
If w <> Nil Then
w.Title = "Updated"
End If
Optional and Result types
Alongside Try/Catch, Buoy provides error-as-value types for cases where you want to propagate absence or failure without exceptions:
Dim result As Optional Of Integer = ParseInt("abc")
If result.HasValue Then
Print result.Value.ToString
End If
Dim r As Result Of String, IOError = File.ReadAllText("/etc/hosts")
If r.IsSuccess Then
Print r.Value
Else
Print r.Error.Message
End If
You are not forced to choose between the two styles — use exceptions for unexpected failures and Optional/Result for expected absence or errors at API boundaries.
Extension methods
You can add methods to any existing type without subclassing:
Extension Function Double Seconds(self As Integer) As Double
Return self * 1.0
End Extension
Callers write 3.Seconds() just like an instance method. This is how the standard library adds instance-method sugar to built-in types (s.Len(), arr.Count()).
Operator overloading
User-defined types can overload arithmetic, comparison, and equality operators using the Operator keyword:
Class Vector2D
Dim X, Y As Double
Operator +(other As Vector2D) As Vector2D
Return New Vector2D(X + other.X, Y + other.Y)
End Operator
End Class
Conditional compilation with architecture predicates
#If directives support both platform and CPU predicates:
#If TargetMac
' macOS-only code
#ElseIf TargetWindows
' Windows-only code
#ElseIf TargetLinux
' Linux-only code
#End If
#If TargetArm
' ARM code (any platform)
#ElseIf TargetIntel
' x86 / x86-64 code
#End If
#Define / #Undef let you define custom predicates. #Error "message" aborts compilation from a branch. Platform-specific Import statements inside an excluded #If branch are not resolved — a missing module in an excluded branch is not an error.
Shared library export
Mark functions as public C-ABI exports with the Export modifier and build a shared library with --library:
Export Function Add(a As Integer, b As Integer) As Integer
Return a + b
End Function
buoy mylib.bui --library -o mylib.dylib
This produces a .dylib / .dll / .so with a C-callable Add symbol. You can use this to build plugins that other languages (including X) can consume. I have already built a cross-platform X Plugin using this mechanism.
Differences in Shared Concepts
FFI / Declare
Both Buoy and X use Declare Function … Lib "…" syntax. Buoy extends it:
| Feature | Buoy | Xojo |
|---|---|---|
| Sized integers | Int16, UInt16, Int32, Int64 for C ABI accuracy |
Limited |
ByRef |
Pass a value by address for C out-parameters | Yes |
| Sequential/Packed structures | Sequential Structure / Packed Structure — exact binary layout for C struct marshalling |
No |
| Variadic C functions | Declare Function Printf Lib "c" (fmt As String, ...) |
No |
| Pointer arithmetic | CType(ptr, Integer) / CType(n, Ptr) / CType(ptr, String) for inline pointer math and C-string extraction |
No |
Soft Declare |
Not available — use #If branches instead |
Yes |
Delegates
Both languages use Delegate Function Name(params) As T and AddressOf. In Buoy, delegates are fully FFI-interoperable:
- A Buoy delegate can be passed directly to a C function expecting a function pointer.
- A function pointer received from C (
As Ptr) can be cast to a delegate type withCType(ptr, DelegateType)and called normally. MarshalToMainThread(AddressOf Foo)synthesizes a C-callable wrapper that marshals a callback onto the main thread.
Objective-C interop (macOS)
Both Buoy and X support Declare … Selector "msg:" for Objective-C message sends. Buoy adds ObjC block literals:
' Inline ObjC block — passed to APIs that expect a block argument
Dim handler As Ptr = Block
Print "callback fired"
End Block
' Dispatch work to the main thread via GCD
Block MainThread
label.Text = "done"
End Block
Guard with #If TargetMac.
Memory management
Both Buoy and X use Automatic Reference Counting — objects are freed deterministically when the last reference drops, with no GC pause.
Buoy additions:
Weak ClassName— weak reference that reads asNilafter the target is freed (breaks retain cycles)buoy --debug— links a leak detector that prints a report at exit (macOS and Linux only)
Current Buoy v1 limitation: ARC is not yet applied to class references stored inside Array or Structure fields. This is planned feature for a future release.
Threading
Buoy provides four additional thread synchronization primitives:
| Primitive | What it does |
|---|---|
Atomic |
Lock-free 64-bit integer: Increment, Decrement, Load, Store, Exchange, CompareAndSet |
Mutex / SyncLock |
Mutual exclusion; SyncLock releases on all exit paths, including exceptions |
Condition |
Condition variable: Wait, Signal, Broadcast — always paired with a predicate loop inside SyncLock |
RunLoop / MainThread |
Main-thread work queue: MainThread.InvokeAsync, InvokeSync; RunLoop.Pump, PumpOnce |
All five primitives lower directly to pthreads (macOS/Linux) or Win32 threads (Windows) with no runtime overhead.
Error handling
Both languages use Try/Catch/Finally/Raise. Buoy also provides Optional Of T and Result Of V, E for error-as-value patterns (see above). There is no forced migration — use exceptions for exceptional conditions and the value types for expected absence or recoverable errors.
String formatting
Buoy’s String.Format supports two styles:
- VB.NET-style format specifiers:
String.Format("{0:0.00}", value) - BASIC picture masks:
String.Format("##.##", value)
Both are accepted.
Task / Await / Async
Task represents a unit of background work. Task.Run spawns an OS thread, runs the given function, and returns an ARC-managed handle. The caller can block until the work finishes with task.Wait() or poll for completion with task.IsComplete().
Mark a Function or Sub with Async to indicate it performs work that may eventually suspend. Call an Async function with Await to retrieve its return value (or wait for it to complete).
Automatic macOS SDK Updates
To update an application to a newer version of the macOS SDK, you only need to update Xcode on your machine and then recompile your bouy app. Minimum macOS versions are still defined as a command-line option.
UI Controls
Buoy implements a native-widget-per-platform philosophy (AppKit on macOS, Win32 on Windows, GTK4 on Linux). Controls minimal but currently working:
| Control | Notes |
|---|---|
Window |
Top-level native window |
Label |
Static text |
Button |
Clickable button |
ImageView |
Displays a Picture |
ProgressBar |
Progress display with Indeterminate mode |
Slider |
Range slider with ValueChanged event |
TextField |
Single-line text input; FieldType enum (Normal, Password, Email, Number) |
Checkbox |
Tri-state toggle (False / True / Indeterminate) |
TextArea |
Multi-line text input |
RadioGroup + RadioButton |
Mutually-exclusive selection |
ComboBox |
Dropdown; pick-only or editable |
ListBox |
Virtualized multi-column grid with ListBoxDataSource data binding |
View |
Container for child controls |
TabView |
Tabbed container |
PagePanel |
Tab-less page stack |
Canvas |
Custom 2D drawing |
MenuBar / Menu / MenuItem |
Application and per-window menus |
Every control exposes a NativeWidget As Ptr property for platform Declares against the raw OS handle (NSView, HWND, GtkWidget).
What Buoy Does Not Have Yet
| Feature | Status |
|---|---|
| Visual IDE / RAD designer | VSCode extension with LSP is available; drag-drop designer is under construction. |
| Drag and drop | Under research |
Xojo Plugin SDK (*.xojo_plugin) |
Buoy can build plugins via Export and --library; it cannot consume existing Xojo plugins |