tiny.zen.diagram.dsl
Defined in diagram.
API (30)
Actions
Public operations.
Figure.document: Copies declarative values into an owned diagram document.Figure.renderAscii: Returns owned ASCII output; release it with the same allocator.barbooleanboxedgefieldfigurelabellayernumberpointrowrulestacktext
Types and contracts
Public types and contracts.
BoxChannelEdgeFieldFigure: Borrowed declarative diagram that can create an owned document or ASCII result.FrameLayerRowRuleScaleScalesStackTransformValue
Source
Source: lib/zen/src/diagram/dsl.zig
zig
const std = @import("std");const root = @import("root.zig");const spec = root.spec;pub const Value = union(enum) { number: f64, text: []const u8, boolean: bool,};pub const Field = struct { name: []const u8, value: Value,};pub const Row = struct { name: []const u8, fields: []const Field,};pub const Frame = struct { width: f64 = 640, height: f64 = 360, inset: f64 = 44, axes: bool = true, title: []const u8 = "", x_label: []const u8 = "", y_label: []const u8 = "", background: []const u8 = "", y_min: ?f64 = null, y_max: ?f64 = null, x_min: ?f64 = null, x_max: ?f64 = null,};pub const Scale = struct { kind: spec.ScaleKind = .linear, min: ?f64 = null, max: ?f64 = null, base: f64 = 10, label: []const u8 = "",};pub const Scales = struct { x: Scale = .{}, y: Scale = .{},};pub const Stack = struct { mark: []const u8 = "bar",};pub const Transform = union(enum) { stack: Stack,};pub const Channel = struct { data: []const u8, x: []const u8, y: []const u8, label: []const u8 = "", fill: []const u8 = "", series: []const u8 = "", text: []const u8 = "", radius: []const u8 = "",};pub const Rule = struct { x1: f64, y1: f64, x2: f64, y2: f64, stroke: []const u8 = "",};pub const Box = struct { x: f64, y: f64, width: f64, height: f64, text: []const u8 = "", fill: []const u8 = "", stroke: []const u8 = "",};pub const Edge = struct { x1: f64, y1: f64, x2: f64, y2: f64, label: []const u8 = "", stroke: []const u8 = "", arrow: bool = true,};pub const Layer = union(enum) { bar: Channel, point: Channel, label: Channel, rule: Rule, box: Box, edge: Edge,};/// Borrowed declarative diagram that can create an owned document or ASCII result.pub const Figure = struct { frame: Frame = .{}, scales: Scales = .{}, transforms: []const Transform = &.{}, data: []const Row = &.{}, layers: []const Layer = &.{}, /// Copies declarative values into an owned diagram document. pub fn document(self: Figure, allocator: std.mem.Allocator) !spec.Document { var result = spec.Document{ .allocator = allocator }; errdefer result.deinit(); result.frame = try copyFrame(allocator, self.frame); result.scales = try copyScales(allocator, self.scales); for (self.transforms) |transform_value| { var copied = try copyTransform(allocator, transform_value); var owned = true; errdefer if (owned) copied.deinit(allocator); try result.transforms.append(allocator, copied); owned = false; } for (self.data) |row_value| { var copied = try copyRow(allocator, row_value); var owned = true; errdefer if (owned) copied.deinit(allocator); try result.data.append(allocator, copied); owned = false; } for (self.layers) |layer_value| try appendLayer(allocator, &result, layer_value); return result; } /// Returns owned ASCII output; release it with the same allocator. pub fn renderAscii( self: Figure, allocator: std.mem.Allocator, options: root.ascii.Options, ) !root.ascii.Rendered { var result = try self.document(allocator); defer result.deinit(); return try root.ascii.renderOwned(allocator, &result, options); }};pub fn figure(value: Figure) Figure { return value;}pub fn row(name: []const u8, fields: []const Field) Row { return .{ .name = name, .fields = fields };}pub fn field(name: []const u8, value: Value) Field { return .{ .name = name, .value = value };}pub fn number(name: []const u8, value: f64) Field { return field(name, .{ .number = value });}pub fn text(name: []const u8, value: []const u8) Field { return field(name, .{ .text = value });}pub fn boolean(name: []const u8, value: bool) Field { return field(name, .{ .boolean = value });}pub fn bar(channel: Channel) Layer { return .{ .bar = channel };}pub fn point(channel: Channel) Layer { return .{ .point = channel };}pub fn label(channel: Channel) Layer { return .{ .label = channel };}pub fn layer(value: Layer) Layer { return value;}pub fn rule(value: Rule) Layer { return .{ .rule = value };}pub fn box(value: Box) Layer { return .{ .box = value };}pub fn edge(value: Edge) Layer { return .{ .edge = value };}pub fn stack() Transform { return .{ .stack = .{} };}fn copyFrame(allocator: std.mem.Allocator, value: Frame) !spec.Frame { var result = spec.Frame{ .width = value.width, .height = value.height, .inset = value.inset, .axes = value.axes, .y_min = value.y_min, .y_max = value.y_max, .x_min = value.x_min, .x_max = value.x_max, }; errdefer result.deinit(allocator); result.title = try copyString(allocator, value.title); result.x_label = try copyString(allocator, value.x_label); result.y_label = try copyString(allocator, value.y_label); result.background = try copyString(allocator, value.background); return result;}fn copyScales(allocator: std.mem.Allocator, value: Scales) !spec.Scales { var result = spec.Scales{}; errdefer result.deinit(allocator); result.x = try copyScale(allocator, value.x); result.y = try copyScale(allocator, value.y); return result;}fn copyScale(allocator: std.mem.Allocator, value: Scale) !spec.Scale { var result = spec.Scale{ .kind = value.kind, .min = value.min, .max = value.max, .base = value.base, }; errdefer result.deinit(allocator); result.label = try copyString(allocator, value.label); return result;}fn copyTransform(allocator: std.mem.Allocator, value: Transform) !spec.Transform { return switch (value) { .stack => |stack_value| .{ .stack = .{ .mark = try copyString(allocator, stack_value.mark) } }, };}fn copyRow(allocator: std.mem.Allocator, value: Row) !spec.DataRow { var result = spec.DataRow{ .name = try copyRequiredString(allocator, value.name) }; errdefer result.deinit(allocator); for (value.fields) |field_value| { var copied = spec.Field{ .name = &.{}, .value = .{ .number = 0 } }; var owned = true; errdefer if (owned) copied.deinit(allocator); copied.name = try copyRequiredString(allocator, field_value.name); copied.value = try copyValue(allocator, field_value.value); try result.fields.append(allocator, copied); owned = false; } if (result.fields.items.len == 0) return error.InvalidData; return result;}fn copyValue(allocator: std.mem.Allocator, value: Value) !spec.DataValue { return switch (value) { .number => |number_value| .{ .number = number_value }, .text => |text_value| .{ .text = try copyString(allocator, text_value) }, .boolean => |boolean_value| .{ .boolean = boolean_value }, };}fn appendLayer(allocator: std.mem.Allocator, document_value: *spec.Document, value: Layer) !void { switch (value) { .bar => |channel| try appendChannelLayer(allocator, document_value, channel, .bar), .point => |channel| try appendChannelLayer(allocator, document_value, channel, .point), .label => |channel| try appendChannelLayer(allocator, document_value, channel, .text), .rule => |rule_value| { var copied = spec.Mark{ .rule = try copyRule(allocator, rule_value) }; var owned = true; errdefer if (owned) copied.deinit(allocator); try document_value.marks.append(allocator, copied); owned = false; }, .box => |box_value| { var copied = spec.Mark{ .box = try copyBox(allocator, box_value) }; var owned = true; errdefer if (owned) copied.deinit(allocator); try document_value.marks.append(allocator, copied); owned = false; }, .edge => |edge_value| { var copied = spec.Mark{ .edge = try copyEdge(allocator, edge_value) }; var owned = true; errdefer if (owned) copied.deinit(allocator); try document_value.marks.append(allocator, copied); owned = false; }, }}const ChannelLayer = enum { bar, point, text,};fn appendChannelLayer(allocator: std.mem.Allocator, document_value: *spec.Document, channel: Channel, kind: ChannelLayer) !void { var matched = false; for (document_value.data.items) |*row_value| { if (!std.mem.eql(u8, row_value.name, channel.data)) continue; matched = true; var mark = try markFromChannel(allocator, channel, row_value, kind); var owned = true; errdefer if (owned) mark.deinit(allocator); try document_value.marks.append(allocator, mark); owned = false; } if (!matched) return error.UnknownData;}fn markFromChannel(allocator: std.mem.Allocator, channel: Channel, row_value: *const spec.DataRow, kind: ChannelLayer) !spec.Mark { return switch (kind) { .bar => .{ .bar = try barFromChannel(allocator, channel, row_value) }, .point => .{ .point = try pointFromChannel(allocator, channel, row_value) }, .text => .{ .text = try textFromChannel(allocator, channel, row_value) }, };}fn barFromChannel(allocator: std.mem.Allocator, channel: Channel, row_value: *const spec.DataRow) !spec.Bar { var result = spec.Bar{ .x = &.{}, .y = 0 }; errdefer result.deinit(allocator); result.x = try requiredText(allocator, row_value, channel.x); result.y = try requiredNumber(row_value, channel.y); result.series = try optionalText(allocator, row_value, channel.series); result.label = try optionalText(allocator, row_value, channel.label); result.fill = try optionalText(allocator, row_value, channel.fill); return result;}fn pointFromChannel(allocator: std.mem.Allocator, channel: Channel, row_value: *const spec.DataRow) !spec.Point { var result = spec.Point{ .x = .{ .number = 0 }, .y = 0 }; errdefer result.deinit(allocator); result.x = try requiredX(allocator, row_value, channel.x); result.y = try requiredNumber(row_value, channel.y); result.label = try optionalText(allocator, row_value, channel.label); result.fill = try optionalText(allocator, row_value, channel.fill); result.radius = if (channel.radius.len == 0) 4 else try requiredNumber(row_value, channel.radius); return result;}fn textFromChannel(allocator: std.mem.Allocator, channel: Channel, row_value: *const spec.DataRow) !spec.Text { if (channel.text.len == 0) return error.MissingField; var result = spec.Text{ .x = .{ .number = 0 }, .y = 0, .text = &.{} }; errdefer result.deinit(allocator); result.x = try requiredX(allocator, row_value, channel.x); result.y = try requiredNumber(row_value, channel.y); result.text = try requiredText(allocator, row_value, channel.text); result.fill = try optionalText(allocator, row_value, channel.fill); return result;}fn copyRule(allocator: std.mem.Allocator, value: Rule) !spec.Rule { var result = spec.Rule{ .x1 = value.x1, .y1 = value.y1, .x2 = value.x2, .y2 = value.y2, }; errdefer result.deinit(allocator); result.stroke = try copyString(allocator, value.stroke); return result;}fn copyBox(allocator: std.mem.Allocator, value: Box) !spec.Box { if (value.width <= 0 or value.height <= 0) return error.InvalidField; var result = spec.Box{ .x = value.x, .y = value.y, .width = value.width, .height = value.height, }; errdefer result.deinit(allocator); result.text = try copyString(allocator, value.text); result.fill = try copyString(allocator, value.fill); result.stroke = try copyString(allocator, value.stroke); return result;}fn copyEdge(allocator: std.mem.Allocator, value: Edge) !spec.Edge { var result = spec.Edge{ .x1 = value.x1, .y1 = value.y1, .x2 = value.x2, .y2 = value.y2, .arrow = value.arrow, }; errdefer result.deinit(allocator); result.label = try copyString(allocator, value.label); result.stroke = try copyString(allocator, value.stroke); return result;}fn requiredValue(row_value: *const spec.DataRow, name: []const u8) !spec.DataValue { if (name.len == 0) return error.MissingField; return row_value.lookup(name) orelse error.MissingField;}fn requiredNumber(row_value: *const spec.DataRow, name: []const u8) !f64 { return numberValue(try requiredValue(row_value, name));}fn requiredText(allocator: std.mem.Allocator, row_value: *const spec.DataRow, name: []const u8) ![]u8 { return textValue(allocator, try requiredValue(row_value, name));}fn requiredX(allocator: std.mem.Allocator, row_value: *const spec.DataRow, name: []const u8) !spec.XValue { return xValue(allocator, try requiredValue(row_value, name));}fn optionalText(allocator: std.mem.Allocator, row_value: *const spec.DataRow, name: []const u8) ![]u8 { if (name.len == 0) return &.{}; return requiredText(allocator, row_value, name);}fn numberValue(value: spec.DataValue) !f64 { return switch (value) { .number => |number_value| number_value, .text => |text_value| std.fmt.parseFloat(f64, text_value) catch error.InvalidField, .boolean => error.InvalidField, };}fn textValue(allocator: std.mem.Allocator, value: spec.DataValue) ![]u8 { return switch (value) { .number => |number_value| try std.fmt.allocPrint(allocator, "{d}", .{number_value}), .text => |text_value| try copyString(allocator, text_value), .boolean => |boolean_value| try copyString(allocator, if (boolean_value) "true" else "false"), };}fn xValue(allocator: std.mem.Allocator, value: spec.DataValue) !spec.XValue { return switch (value) { .number => |number_value| .{ .number = number_value }, .text => |text_value| .{ .text = try copyString(allocator, text_value) }, .boolean => |boolean_value| .{ .text = try copyString(allocator, if (boolean_value) "true" else "false") }, };}fn copyRequiredString(allocator: std.mem.Allocator, value: []const u8) ![]u8 { if (value.len == 0) return error.MissingField; return copyString(allocator, value);}fn copyString(allocator: std.mem.Allocator, value: []const u8) ![]u8 { if (value.len == 0) return &.{}; return try allocator.dupe(u8, value);}test "diagram DSL renders data-driven bars as ASCII" { const plot = figure(.{ .frame = .{ .title = "Coin", .y_min = 0, .y_max = 1, .x_label = "outcome", .y_label = "p" }, .data = &.{ row("coin", &.{ text("outcome", "heads"), number("probability", 0.62), text("label", "heads") }), row("coin", &.{ text("outcome", "tails"), number("probability", 0.38), text("label", "tails") }), }, .layers = &.{ bar(.{ .data = "coin", .x = "outcome", .y = "probability", .label = "label" }), }, }); var rendered = try plot.renderAscii(std.testing.allocator, .{ .width = 40, .height = 12 }); defer rendered.deinit(std.testing.allocator); try std.testing.expect(std.mem.indexOf(u8, rendered.output, "Coin") != null); try std.testing.expect(std.mem.indexOf(u8, rendered.output, "heads") != null); try std.testing.expect(std.mem.indexOf(u8, rendered.output, "#") != null);}Source: lib/zen/src/diagram/root.zig:6
zig
pub const dsl = @import("dsl.zig");Audit
| Definitions | 31 |
|---|---|
| Public names | 51 |
| Members | 66 |
| Version | 26.7.0 |
| Revision | daab053ee433 |