Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 38 additions & 17 deletions dsl/extdeps/render/ansi.dag
Original file line number Diff line number Diff line change
@@ -1,36 +1,57 @@
// extdeps/render/ansi.dag — ANSI escape / SGR color realization for SemanticColor.
//
// Anchor: https://en.wikipedia.org/wiki/ANSI_escape_code
// Anchor: https://ecma-international.org/publications-and-standards/standards/ecma-48/
// (ISO/IEC 6429 Select Graphic Rendition CSI sequences: ESC [ <params> m)
//
// Upstream: ECMA-48 (ISO/IEC 6429) / ANSI X3.64 — control sequences for character
// imputation. The cited escape strings are Select Graphic Rendition (SGR) CSI
// sequences (`ESC [ … m`); 256-color foreground uses `38;5;<n>` (ISO 8613-6
// indexed palette). Real spec names kept in the `code` field.
// Provenance: gunb.ai/pkg/fermi/colors.go — project palette-index choices per SemanticColor.
//
// P2 split: std.symbols owns the agnostic SemanticColor concept; the cited SGR
// escape sequences live here. Project palette index choices per semantic role
// align with gunb.ai/pkg/fermi/colors.go for consistent appearance.
// realization lives here.

module extdeps.render.ansi

import std.symbols { SemanticColor }

// ECMA-48 CSI framing — emitted once via emit_sgr_csi, not repeated per mapping row.
data csi_esc: String = "\x1b["
data csi_end: String = "m"

// ISO 8613-6 / xterm 256-color palette index (0..255).
type PaletteIndex = Int

type SgrStyle
= SgrReset {}
| SgrDim {}
| SgrIndexedForeground { index: PaletteIndex }

type AnsiMapping {
color: SemanticColor
code: String
style: SgrStyle
}

data ansi_mappings: List<AnsiMapping> = [
{ color: Default, code: "\x1b[0m" },
{ color: Success, code: "\x1b[38;5;34m" },
{ color: Warning, code: "\x1b[38;5;208m" },
{ color: Error, code: "\x1b[38;5;196m" },
{ color: Info, code: "\x1b[38;5;39m" },
{ color: Dim, code: "\x1b[2m" },
{ color: Active, code: "\x1b[38;5;208m" },
{ color: Accent, code: "\x1b[38;5;75m" }
{ color: Default, style: SgrReset {} },
{ color: Success, style: SgrIndexedForeground { index: 34 } },
{ color: Warning, style: SgrIndexedForeground { index: 208 } },
{ color: Error, style: SgrIndexedForeground { index: 196 } },
{ color: Info, style: SgrIndexedForeground { index: 39 } },
{ color: Dim, style: SgrDim {} },
{ color: Active, style: SgrIndexedForeground { index: 208 } },
{ color: Accent, style: SgrIndexedForeground { index: 75 } }
]

fn format_sgr_params(style: SgrStyle) -> String {
match style {
SgrReset {} => "0"
SgrDim {} => "2"
SgrIndexedForeground { index: n } => concat("38;5;", to_string(n))
}
}

fn emit_sgr_csi(style: SgrStyle) -> String {
concat(concat(csi_esc, format_sgr_params(style: style)), csi_end)
}

// Lookup via fold + structural == (filter+field-equality hits a v1 interpreter bug).
fn ansi_code(c: SemanticColor) -> String? {
fold(ansi_mappings,
Expand All @@ -39,7 +60,7 @@ fn ansi_code(c: SemanticColor) -> String? {
Present { value: _ } => acc
Absent =>
if m.color == c {
Present { value: m.code }
Present { value: emit_sgr_csi(style: m.style) }
} else {
none
}
Expand Down
18 changes: 18 additions & 0 deletions dsl/extdeps/render/ascii_glyph.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// extdeps/render/ascii_glyph.dag — ASCII-tier glyph carrier (ECMA-6 repertoire).
//
// Anchor: https://www.ecma-international.org/publications-and-standards/standards/ecma-6/
// (ECMA-6 / ISO 646 7-bit coded character set; RFC 20 is the IETF mirror)
//
// dissolve-on: Q-Regex repertoire refinement — ASCII printable 0x20–0x7E.

module extdeps.render.ascii_glyph

type AsciiGlyph = String where brand("AsciiGlyph")

fn ascii_glyph(text: String) -> AsciiGlyph {
text
}

fn ascii_glyph_text(g: AsciiGlyph) -> String {
g
}
17 changes: 17 additions & 0 deletions dsl/extdeps/render/emoji_glyph.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// extdeps/render/emoji_glyph.dag — Emoji-tier glyph carrier (UTS #51).
//
// Anchor: https://www.unicode.org/reports/tr51/ (UTS #51 Unicode Emoji)
//
// dissolve-on: Q-Regex UTS #51 emoji-sequence validity.

module extdeps.render.emoji_glyph

type EmojiGlyph = String where brand("EmojiGlyph")

fn emoji_glyph(text: String) -> EmojiGlyph {
text
}

fn emoji_glyph_text(g: EmojiGlyph) -> String {
g
}
101 changes: 53 additions & 48 deletions dsl/extdeps/render/glyphs.dag
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@
//
// Project visual vocabulary: three-tier encodings (Emoji / Unicode / Ascii) per
// SymbolId. Dispatch lives in extdeps/, not std/ (DESIGN.md §3): std.symbols
// owns only the agnostic glyph identity and SymbolEntry shape.
// owns only the agnostic glyph identity vocabulary (SymbolId, Tier, SemanticColor).

module extdeps.render.glyphs

import std.symbols { SemanticColor, SymbolEntry, SymbolId, Tier }
import extdeps.render.ascii_glyph { ascii_glyph }
import extdeps.render.emoji_glyph { emoji_glyph }
import extdeps.render.unicode_glyph { unicode_glyph }
import extdeps.render.tier_glyphs { TierGlyphs, glyph_at, glyphs_for_tiers }
import std.symbols { SemanticColor, SymbolId, Tier }

type SymbolEntry {
id: SymbolId
glyphs: TierGlyphs
color: SemanticColor
}

// --- Standard symbol set ---
//
Expand All @@ -16,60 +26,60 @@ import std.symbols { SemanticColor, SymbolEntry, SymbolId, Tier }
data standard_symbols: List<SymbolEntry> = [
// Node lifecycle
// NOTE: emoji/unicode tiers collapsed to ASCII -- restore when tokenizer handles UTF-8
{ id: NodePending, emoji: "[ ]", unicode: "o", ascii: "[ ]", color: Dim },
{ id: NodeRunning, emoji: "[~]", unicode: "O", ascii: "[~]", color: Active },
{ id: NodeCompleted, emoji: "[x]", unicode: "*", ascii: "[x]", color: Success },
{ id: NodeFailed, emoji: "[!]", unicode: "x", ascii: "[!]", color: Error },
{ id: NodeSkipped, emoji: "[-]", unicode: "o", ascii: "[-]", color: Dim },
{ id: NodeIntercepted, emoji: "[m]", unicode: "<>", ascii: "[m]", color: Info },
{ id: NodePending, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[ ]"), unicode: unicode_glyph(text: "o"), ascii: ascii_glyph(text: "[ ]")), color: Dim },
{ id: NodeRunning, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[~]"), unicode: unicode_glyph(text: "O"), ascii: ascii_glyph(text: "[~]")), color: Active },
{ id: NodeCompleted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[x]"), unicode: unicode_glyph(text: "*"), ascii: ascii_glyph(text: "[x]")), color: Success },
{ id: NodeFailed, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[!]"), unicode: unicode_glyph(text: "x"), ascii: ascii_glyph(text: "[!]")), color: Error },
{ id: NodeSkipped, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[-]"), unicode: unicode_glyph(text: "o"), ascii: ascii_glyph(text: "[-]")), color: Dim },
{ id: NodeIntercepted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[m]"), unicode: unicode_glyph(text: "<>"), ascii: ascii_glyph(text: "[m]")), color: Info },

// Edge states
{ id: EdgeIdle, emoji: "-", unicode: "-", ascii: "-", color: Dim },
{ id: EdgeFlowing, emoji: "=", unicode: "=", ascii: "=", color: Accent },
{ id: EdgeDone, emoji: "-", unicode: "-", ascii: "-", color: Success },
{ id: EdgeDead, emoji: ".", unicode: ".", ascii: ".", color: Dim },
{ id: EdgeIdle, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Dim },
{ id: EdgeFlowing, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "="), unicode: unicode_glyph(text: "="), ascii: ascii_glyph(text: "=")), color: Accent },
{ id: EdgeDone, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Success },
{ id: EdgeDead, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "."), unicode: unicode_glyph(text: "."), ascii: ascii_glyph(text: ".")), color: Dim },

// DAG phases
{ id: DagNotStarted, emoji: "[ ]", unicode: "[ ]", ascii: "[ ]", color: Dim },
{ id: DagRunning, emoji: "[>]", unicode: ">", ascii: "[>]", color: Active },
{ id: DagCompleted, emoji: "[X]", unicode: "#", ascii: "[X]", color: Success },
{ id: DagFailed, emoji: "[!]", unicode: "#", ascii: "[!]", color: Error },
{ id: DagNotStarted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[ ]"), unicode: unicode_glyph(text: "[ ]"), ascii: ascii_glyph(text: "[ ]")), color: Dim },
{ id: DagRunning, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[>]"), unicode: unicode_glyph(text: ">"), ascii: ascii_glyph(text: "[>]")), color: Active },
{ id: DagCompleted, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[X]"), unicode: unicode_glyph(text: "#"), ascii: ascii_glyph(text: "[X]")), color: Success },
{ id: DagFailed, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[!]"), unicode: unicode_glyph(text: "#"), ascii: ascii_glyph(text: "[!]")), color: Error },

// Structural
{ id: BoundaryMarker, emoji: "[B]", unicode: "<>", ascii: "[B]", color: Info },
{ id: BoundaryMarker, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[B]"), unicode: unicode_glyph(text: "<>"), ascii: ascii_glyph(text: "[B]")), color: Info },

// Spinners (ASCII fallback -- restore braille when tokenizer handles UTF-8)
{ id: Spinner0, emoji: "|", unicode: "|", ascii: "|", color: Active },
{ id: Spinner1, emoji: "/", unicode: "/", ascii: "/", color: Active },
{ id: Spinner2, emoji: "-", unicode: "-", ascii: "-", color: Active },
{ id: Spinner3, emoji: "\\", unicode: "\\", ascii: "\\", color: Active },
{ id: Spinner4, emoji: "|", unicode: "|", ascii: "|", color: Active },
{ id: Spinner5, emoji: "/", unicode: "/", ascii: "/", color: Active },
{ id: Spinner6, emoji: "-", unicode: "-", ascii: "-", color: Active },
{ id: Spinner7, emoji: "\\", unicode: "\\", ascii: "\\", color: Active },
{ id: Spinner8, emoji: "|", unicode: "|", ascii: "|", color: Active },
{ id: Spinner9, emoji: "/", unicode: "/", ascii: "/", color: Active },
{ id: Spinner0, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Active },
{ id: Spinner1, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "/"), unicode: unicode_glyph(text: "/"), ascii: ascii_glyph(text: "/")), color: Active },
{ id: Spinner2, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Active },
{ id: Spinner3, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "\\"), unicode: unicode_glyph(text: "\\"), ascii: ascii_glyph(text: "\\")), color: Active },
{ id: Spinner4, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Active },
{ id: Spinner5, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "/"), unicode: unicode_glyph(text: "/"), ascii: ascii_glyph(text: "/")), color: Active },
{ id: Spinner6, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "-"), unicode: unicode_glyph(text: "-"), ascii: ascii_glyph(text: "-")), color: Active },
{ id: Spinner7, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "\\"), unicode: unicode_glyph(text: "\\"), ascii: ascii_glyph(text: "\\")), color: Active },
{ id: Spinner8, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Active },
{ id: Spinner9, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "/"), unicode: unicode_glyph(text: "/"), ascii: ascii_glyph(text: "/")), color: Active },

// Status indicators
{ id: StatusSuccess, emoji: "OK", unicode: "v", ascii: "OK", color: Success },
{ id: StatusFailure, emoji: "FAIL", unicode: "x", ascii: "FAIL", color: Error },
{ id: StatusWarning, emoji: "WARN", unicode: "!", ascii: "WARN", color: Warning },
{ id: StatusInfo, emoji: "INFO", unicode: "i", ascii: "INFO", color: Info },
{ id: StatusSuccess, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "OK"), unicode: unicode_glyph(text: "v"), ascii: ascii_glyph(text: "OK")), color: Success },
{ id: StatusFailure, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "FAIL"), unicode: unicode_glyph(text: "x"), ascii: ascii_glyph(text: "FAIL")), color: Error },
{ id: StatusWarning, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "WARN"), unicode: unicode_glyph(text: "!"), ascii: ascii_glyph(text: "WARN")), color: Warning },
{ id: StatusInfo, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "INFO"), unicode: unicode_glyph(text: "i"), ascii: ascii_glyph(text: "INFO")), color: Info },

// Data type indicators
{ id: DataList, emoji: "[L]", unicode: "=", ascii: "[L]", color: Default },
{ id: DataMap, emoji: "[M]", unicode: "[+]", ascii: "[M]", color: Default },
{ id: DataSecret, emoji: "[*]", unicode: "*", ascii: "[*]", color: Warning },
{ id: DataUrl, emoji: "[U]", unicode: ">", ascii: "[U]", color: Info },
{ id: DataTimer, emoji: "[T]", unicode: "[T]", ascii: "[T]", color: Dim },
{ id: DataList, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[L]"), unicode: unicode_glyph(text: "="), ascii: ascii_glyph(text: "[L]")), color: Default },
{ id: DataMap, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[M]"), unicode: unicode_glyph(text: "[+]"), ascii: ascii_glyph(text: "[M]")), color: Default },
{ id: DataSecret, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[*]"), unicode: unicode_glyph(text: "*"), ascii: ascii_glyph(text: "[*]")), color: Warning },
{ id: DataUrl, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[U]"), unicode: unicode_glyph(text: ">"), ascii: ascii_glyph(text: "[U]")), color: Info },
{ id: DataTimer, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "[T]"), unicode: unicode_glyph(text: "[T]"), ascii: ascii_glyph(text: "[T]")), color: Dim },

// Connectors
{ id: ConnectorHorizontal, emoji: "--", unicode: "--", ascii: "--", color: Dim },
{ id: ConnectorVertical, emoji: "|", unicode: "|", ascii: "|", color: Dim },
{ id: ConnectorTeeDown, emoji: "+", unicode: "+", ascii: "+", color: Dim },
{ id: ConnectorTeeUp, emoji: "+", unicode: "+", ascii: "+", color: Dim },
{ id: ConnectorCornerBottomLeft, emoji: "+", unicode: "+", ascii: "+", color: Dim },
{ id: ConnectorCornerTopLeft, emoji: "+", unicode: "+", ascii: "+", color: Dim }
{ id: ConnectorHorizontal, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "--"), unicode: unicode_glyph(text: "--"), ascii: ascii_glyph(text: "--")), color: Dim },
{ id: ConnectorVertical, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "|"), unicode: unicode_glyph(text: "|"), ascii: ascii_glyph(text: "|")), color: Dim },
{ id: ConnectorTeeDown, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim },
{ id: ConnectorTeeUp, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim },
{ id: ConnectorCornerBottomLeft, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim },
{ id: ConnectorCornerTopLeft, glyphs: glyphs_for_tiers(emoji: emoji_glyph(text: "+"), unicode: unicode_glyph(text: "+"), ascii: ascii_glyph(text: "+")), color: Dim }
]

// Lookup via fold + structural == (filter+field-equality hits a v1 interpreter bug).
Expand All @@ -90,12 +100,7 @@ fn lookup_symbol_entry(id: SymbolId) -> SymbolEntry? {

fn resolve_symbol(id: SymbolId, tier: Tier) -> String? {
match lookup_symbol_entry(id: id) {
Present { value: entry } =>
match tier {
Emoji => Present { value: entry.emoji }
Unicode => Present { value: entry.unicode }
Ascii => Present { value: entry.ascii }
}
Present { value: entry } => Present { value: glyph_at(glyphs: entry.glyphs, tier: tier) }
Absent => none
}
}
Expand Down
2 changes: 2 additions & 0 deletions dsl/extdeps/render/terminal.dag
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// extdeps/render/terminal.dag — Terminal width composition over glyph realization.
//
// Anchor: https://www.unicode.org/reports/tr11/ (UAX #11 East Asian Width; sibling to std.unicode)
//
// Width-constrained frame/line composition depends on resolve_symbol (encoding-tier
// dispatch in extdeps.render.glyphs). Lives in extdeps so std.render stays
// realization-free (DESIGN.md §3).
Expand Down
29 changes: 29 additions & 0 deletions dsl/extdeps/render/tier_glyphs.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// extdeps/render/tier_glyphs.dag — Total Tier→glyph product (one field per variant).
//
// Each field is a distinct encoding authority (ascii_glyph / unicode_glyph / emoji_glyph).
// Illegal states (missing/duplicate tier) are unrepresentable.

module extdeps.render.tier_glyphs

import extdeps.render.ascii_glyph { AsciiGlyph, ascii_glyph_text }
import extdeps.render.emoji_glyph { EmojiGlyph, emoji_glyph_text }
import extdeps.render.unicode_glyph { UnicodeGlyph, unicode_glyph_text }
import std.symbols { Tier }

type TierGlyphs {
ascii: AsciiGlyph
unicode: UnicodeGlyph
emoji: EmojiGlyph
}

fn glyphs_for_tiers(emoji: EmojiGlyph, unicode: UnicodeGlyph, ascii: AsciiGlyph) -> TierGlyphs {
{ emoji: emoji, unicode: unicode, ascii: ascii }
}

fn glyph_at(glyphs: TierGlyphs, tier: Tier) -> String {
match tier {
Emoji => emoji_glyph_text(g: glyphs.emoji)
Unicode => unicode_glyph_text(g: glyphs.unicode)
Ascii => ascii_glyph_text(g: glyphs.ascii)
}
}
26 changes: 26 additions & 0 deletions dsl/extdeps/render/unicode_glyph.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// extdeps/render/unicode_glyph.dag — Unicode-tier glyph carrier.
//
// Anchor: https://www.unicode.org/versions/latest/
// (Unicode Standard — same authority as std.unicode UCD 15.x; no parallel model)
//
// dissolve-on: Q-Regex repertoire refinement — cited blocks (U+2500 Box Drawing,
// U+2800 Braille Patterns, …).

module extdeps.render.unicode_glyph

import std.unicode { string_display_width }

type UnicodeGlyph = String where brand("UnicodeGlyph")

fn unicode_glyph(text: String) -> UnicodeGlyph {
text
}

fn unicode_glyph_text(g: UnicodeGlyph) -> String {
g
}

// Display-width dispatch for Unicode-tier glyphs defers to std.unicode (UAX #11).
fn unicode_glyph_display_width(g: UnicodeGlyph) -> Int {
string_display_width(s: unicode_glyph_text(g: g))
}
18 changes: 3 additions & 15 deletions dsl/std/symbols.dag
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ type SemanticColor = Default | Success | Warning | Error | Info | Dim | Active |

// --- Encoding tier ---
//
// Ordered from richest to most compatible:
// Emoji > Unicode > Ascii
// Project interface: ordered richness (Emoji > Unicode > Ascii).
// Not an external authority — tier dispatch contract lives in std; cited
// repertoire per tier is anchored in extdeps/render/glyphs.dag.

type Tier = Emoji | Unicode | Ascii

Expand Down Expand Up @@ -84,16 +85,3 @@ type SymbolId
| ConnectorTeeUp
| ConnectorCornerBottomLeft
| ConnectorCornerTopLeft

// --- Symbol entry ---
//
// A single symbol with all three tier encodings and a semantic color.
// The cited realization rows live in extdeps/render/glyphs.dag.

type SymbolEntry {
id: SymbolId
emoji: String
unicode: String
ascii: String
color: SemanticColor
}
Loading