Drivers
The native drivers that provide the interface, console and network.
Contents
How a Program becomes Zig, in parts. Start with the introduction, then follow each part into the rendered output and the runtime it links against.
- Introduction — what the Zig target is, and how to read the rest of this section.
- Capabilities — which high-level features are possible, built in or through a library.
- Example — a full program using every keyword, and the Zig it renders to.
- Naming considerations — how Program's dotted names are carried into Zig.
- Entry point — starting a program from
mainwith its drivers. - Drivers — the native drivers that provide the interface, console and network.
- Runtime — the Reactive and Context machinery the rendered code links against.
- Native modules — the hand-written Zig behind
NATIVEfunctions. - Keywords — how each of Program's keywords renders to Zig.
- Project setup — the files and commands to build and run the rendered program.
- Reserved words — the words Zig keeps for itself, and how a colliding name is escaped.
Defining a driver
A driver is a Zig struct with the driver's functions: it drains one context channel and reconciles
the resources it owns. Here is a console driver — it drains the Console.Lines
channel and prints each line that has changed since the last cycle.
const ConsoleDriver = struct {
last_by_id: std.AutoHashMap(i64, []const u8),
pub fn channel(_: *ConsoleDriver) []const u8 {
return "Console.Lines";
}
pub fn kind(_: *ConsoleDriver) DriverKind {
return .array;
}
pub fn init(_: *ConsoleDriver, add_updates: AddUpdates) void {
_ = add_updates;
}
pub fn dispatch(self: *ConsoleDriver, emissions: []const Line) void {
for (emissions) |line| {
const prev = self.last_by_id.get(line.id);
if (prev == null or !std.mem.eql(u8, prev.?, line.text)) {
std.debug.print("{s}\n", .{line.text});
self.last_by_id.put(line.id, line.text) catch {};
}
}
}
};
Registering it
A program lists the drivers it uses; the runtime opens a channel for each and never hardcodes one.
pub fn main() !void {
var console = ConsoleDriver{ .last_by_id = std.AutoHashMap(i64, []const u8).init(allocator) };
Program.application(struct { pub fn run() void {
Main();
} }.run, &.{ console.driver() });
}
How the runtime runs it
Each cycle the runtime opens the driver's channel, runs the reactive program (which emits into it),
drains it, and passes the slice to dispatch:
Context.openArray("Console.Lines");
program = Reactive.run(program, updates);
driver.dispatch(Context.drainArray("Console.Lines"));
A driver that produces events — a timer, a network reply — calls the
add_updates it was given in init, which schedules the next cycle. The three
shapes are the same as everywhere: a tree renderer (the terminal interface), a request set (network),
and a shared ticker.