Something went wrong. Try again.
scipting language written in zig
Something went wrong. Try again.
33 kB · 1032 lines
HTML
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <title>Runic Language Reference</title> <style> * { box-sizing: border-box; }
body { font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; margin: 0; background: #0f172a; color: #e2e8f0; line-height: 1.6; padding: 2rem; }
header { max-width: 900px; margin: 0 auto 2rem; text-align: center; }
h1 { margin: 0 0 0.5rem; font-size: 2.75rem; letter-spacing: -0.04em; }
header p { margin: 0 auto 1rem; max-width: 720px; color: #94a3b8; }
.search { margin-top: 1.5rem; display: flex; flex-direction: column; gap: 0.4rem; text-align: left; }
.search label { font-weight: 600; }
.search input { padding: 0.8rem 1rem; border-radius: 0.75rem; border: 1px solid rgba(148, 163, 184, 0.3); background: rgba(15, 23, 42, 0.6); color: inherit; font-size: 1rem; transition: border 0.2s ease, box-shadow 0.2s ease; }
.search input:focus { outline: none; border-color: #38bdf8; box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.25); }
.content { max-width: 1400px; margin: 0 auto; display: flex; gap: 2rem; align-items: flex-start; }
nav.sidebar { position: sticky; top: 2rem; width: 260px; flex-shrink: 0; background: rgba(15, 23, 42, 0.85); border: 1px solid rgba(59, 130, 246, 0.3); border-radius: 1rem; padding: 1.25rem; box-shadow: 0 10px 35px rgba(2, 6, 23, 0.5); max-height: calc(100vh - 4rem); overflow-y: auto; }
.sidebar-title { margin: 0 0 1rem; font-size: 1.2rem; text-transform: uppercase; letter-spacing: 0.08em; color: #94a3b8; }
.sidebar-group + .sidebar-group { margin-top: 1.5rem; }
.sidebar-group h3 { margin: 0 0 0.5rem; font-size: 1rem; color: #e0e7ff; text-transform: uppercase; letter-spacing: 0.08em; }
.sidebar-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.sidebar-list a { color: #cbd5f5; text-decoration: none; font-size: 0.95rem; padding: 0.3rem 0.4rem; border-radius: 0.5rem; display: block; transition: background 0.2s ease, color 0.2s ease; }
.sidebar-list a:hover, .sidebar-list a:focus { background: rgba(59, 130, 246, 0.2); color: #e2e8f0; outline: none; }
main { flex: 1; max-width: none; display: flex; flex-direction: column; gap: 3rem; }
section h2 { margin: 0 0 0.6rem; font-size: 1.8rem; }
section > p { margin: 0 0 1rem; color: #a5b4fc; }
.entry-list { display: flex; flex-direction: column; gap: 2rem; }
article.entry { padding: 1.5rem 1.5rem 1.5rem 1.75rem; background: rgba(15, 23, 42, 0.75); border-left: 4px solid rgba(59, 130, 246, 0.5); border-radius: 0.85rem; box-shadow: 0 6px 18px rgba(2, 6, 23, 0.55); display: flex; flex-direction: column; gap: 0.85rem; }
article.entry h3 { margin: 0; font-size: 1.3rem; }
.badge { display: inline-flex; align-items: center; padding: 0.15rem 0.45rem; border-radius: 999px; font-size: 0.75rem; text-transform: uppercase; font-weight: 700; letter-spacing: 0.05em; }
.badge-type { background: rgba(16, 185, 129, 0.15); color: #34d399; }
.badge-feature { background: rgba(248, 113, 113, 0.15); color: #f87171; }
pre { margin: 0; padding: 0.75rem; border-radius: 0.75rem; background: #0b1121; overflow-x: auto; font-size: 0.9rem; }
code { font-family: "JetBrains Mono", "Fira Code", Menlo, Consolas, monospace; }
.example-label { text-transform: uppercase; font-size: 0.7rem; letter-spacing: 0.08em; color: #cbd5f5; margin-bottom: 0.25rem; }
.empty-state { text-align: center; color: #fca5a5; font-weight: 600; }
.is-hidden { display: none !important; }
@media (max-width: 900px) { .content { flex-direction: column; }
nav.sidebar { position: static; width: 100%; } }
@media (max-width: 600px) { body { padding: 1.5rem 1rem; }
h1 { font-size: 2.2rem; } } </style> </head> <body> <header> <h1>Runic Language Reference</h1> <p> A quick reference to Runic’s built-in types and language features. Every entry includes a one-line summary plus an executable snippet pulled from the current design docs so you can mirror the same behavior in your scripts. </p> <div class="search"> <label for="search">Search by name, keyword, or capability</label> <input id="search" type="search" placeholder="e.g. promise, pipeline, map, async" autocomplete="off" /> </div> </header>
<div class="content"> <nav class="sidebar" aria-label="Reference navigation"> <p class="sidebar-title">Jump to</p> <div class="sidebar-group"> <h3>Types</h3> <ul id="sidebar-types" class="sidebar-list"></ul> </div> <div class="sidebar-group"> <h3>Features</h3> <ul id="sidebar-features" class="sidebar-list"></ul> </div> </nav>
<main> <section id="types"> <h2>Built-in Types</h2> <p> Runic values default to predictable semantics: immutable bindings, explicit mutation, and Zig-inspired wrappers like <code>?T</code>, <code>!T</code>, and <code>^T</code>. </p> <div class="entry-list"> <article class="entry" data-searchable data-keywords="str string text primitive" > <h3><span class="badge badge-type">Type</span> Str</h3> <p> Immutable UTF-8 strings. Use <code>Str</code> for command arguments, logging, and any textual data. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let greeting: Str = "hello world"echo greeting.upper()</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="int integer number primitive mut" > <h3><span class="badge badge-type">Type</span> Int</h3> <p> Signed integers that keep arithmetic explicit. Bindings stay immutable unless you declare them with <code>mut</code>. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">mut retries: Int = 2retries = retries + 1</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="float number primitive decimal" > <h3><span class="badge badge-type">Type</span> Float</h3> <p> Floating-point numbers for precise ratios or measurements. Annotate literal math so conversions are enforced up front. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let pi: Float = 3.14159const radius: Float = 2.0const area = pi * radius * radius</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="bool boolean predicate primitive condition" > <h3><span class="badge badge-type">Type</span> Bool</h3> <p> Logical true/false values that drive conditionals. Distinguish command success from custom business logic. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let count: Int = 3const is_plural: Bool = count > 1if is_plural { echo "plural branch"} else { echo "singular branch"}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="bytes binary payload primitive" > <h3><span class="badge badge-type">Type</span> Bytes</h3> <p> Opaque byte buffers for binary payloads such as HTTP bodies or archive blobs. Keep text/bytes conversions explicit. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let http = import("net/http")
const resp = http.get("https://example.com/status")const payload: Bytes = resp.body</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="void unit return type fn" > <h3><span class="badge badge-type">Type</span> Void</h3> <p> Indicates a function returns no value beyond success. Pair it with <code>!Void</code> to signal commands that only fail or succeed. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">fn init() !Void { try bootstrap_network() echo "Runtime ready"}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="pid process identifier primitive" > <h3><span class="badge badge-type">Type</span> Pid</h3> <p> Process identifiers exposed by command handles so you can introspect or manage background work. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let server = tail -f /var/log/app.log &echo "spawned ${server.pid}"</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="exit status code primitive process" > <h3><span class="badge badge-type">Type</span> ExitStatus</h3> <p> Structured command results that include the exit code plus helpers such as <code>.ok</code> and <code>.failed_stage</code>. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let status: ExitStatus = (build | tee build.log).statusif !status.ok { echo "Pipeline failed at ${status.failed_stage}"}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="array list collection literal" > <h3><span class="badge badge-type">Type</span> Array<T></h3> <p> Homogeneous, indexable collections that interact with <code>for</code> loops and iterators. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let fruits: Array(Str) = ["apple", "banana", "pear"]for (fruits, 0..) |fruit, idx| { echo "${idx}: ${fruit.upper()}"}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="map dictionary record literal" > <h3> <span class="badge badge-type">Type</span> Map<K, V> </h3> <p> Key/value dictionaries with literal syntax that preserves structure across commands. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let config: Map(Str, Int) = { port: 8080, retries: 2 }echo "Listening on ${config.port}"</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="process handle stdout stderr command type" > <h3><span class="badge badge-type">Type</span> ProcessHandle</h3> <p> Structured results returned by every command invocation. Access <code>stdout</code>, <code>stderr</code>, <code>status</code>, and metadata without shell globals, even when command-producing expressions are chained with <code>&&</code>, <code>||</code>, or <code>;</code>. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">const combined = printf "hello\n" && printf "warning\n" >&2echo combined.stdoutecho combined.stderr
const sequenced = printf "hello\n"; printf "warning\n" >&2echo sequenced.stdoutecho sequenced.stderrecho sequenced.status.exit_code</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="error set union !t enum union type" > <h3> <span class="badge badge-type">Type</span>Error Sets & <code>!T</code> </h3> <p> Declare explicit error families via <code>error</code> enums or unions, then return <code>!T</code> to describe “error or value” results that require handling. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">error FileError = union { NotFound: { path: Str }, PermissionDenied: { user: Str },}
fn read_config(path: Str) FileError!Config { if !file.exists(path) { return error.FileError.NotFound { path } } return parse_config(path)}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="optional maybe ?t null type" > <h3> <span class="badge badge-type">Type</span> Optionals (<code>?T</code>) </h3> <p> Wrap any type with <code>?T</code> to express nullable values; use optional-aware <code>if</code> captures to unwrap safely. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">const maybe_port: ?String = $PORTif (maybe_port) |port| { echo "Running on port ${port}"} else { echo "Using default port"}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="background execution wait type concurrency" > <h3> <span class="badge badge-type">Type</span> Background executions </h3> <p> A trailing <code>&</code> starts a command, pipeline, or block expression in the background. When you bind that result, its output stays buffered in memory and <code>.wait</code> blocks until it finishes. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">(sleep 0.05; echo "warmup") &echo "continued"
const job = (sleep 0.05; echo "hello from job" &)job.waitprintf "%s" "${job.stdout}"</code></pre> </div> </article> </div> </section>
<section id="features"> <h2>Language Features</h2> <p> Feature highlights mirror <code>features.md</code> so you can cross-reference the philosophy and the syntax. </p> <div class="entry-list"> <article class="entry" data-searchable data-keywords="command first pipelines shell friendly" > <h3> <span class="badge badge-feature">Feature</span> Command-first execution </h3> <p> Every bare word starts a pipeline stage, keeping shell muscle memory intact while layering structured data on top. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">echo "hello world" | upper</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="data semantics let mut typed variables" > <h3> <span class="badge badge-feature">Feature</span> Strong, predictable data semantics </h3> <p> Immutable <code>let</code> bindings, opt-in <code>mut</code>, and literal arrays/maps eliminate accidental word splitting. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let greeting = "hello"var count = 2const items = ["apples", "oranges"]const vars = { greeting: greeting, total: count }</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="error handling try catch first class" > <h3> <span class="badge badge-feature">Feature</span> Errors as first-class types </h3> <p> Model recoverable failures via typed error sets and handle them explicitly with <code>catch</code> instead of parsing strings. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">const config = load_config() catch |err| { echo "load failed: ${err}"}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="try catch mandatory handling" > <h3> <span class="badge badge-feature">Feature</span> Mandatory explicit handling </h3> <p> Every error is either propagated with <code>try</code> or handled with <code>catch</code>, mirroring Zig’s guarantees. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">fn init() !Void { try bootstrap_network()
read_config("/etc/runic.conf") catch |err| { if err == error.FileError.NotFound { return err } echo "Recovered from ${err}" }}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="error sets restrict function subset" > <h3> <span class="badge badge-feature">Feature</span> Restrict function error sets </h3> <p> Constrain return signatures (e.g. <code>FileError!Config</code>) so downstream code knows the precise failures to match. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">fn read_config(path: Str) FileError!Config { if !file.exists(path) { return error.FileError.NotFound { path } } return parse_config(path)}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="optional zig style null capture clause" > <h3> <span class="badge badge-feature">Feature</span> Optional data that behaves like Zig </h3> <p> <code>?T</code> pairs with optional-aware <code>if</code> so values only exist inside the capture scope when present. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">const maybe_port = $PORTif (maybe_port) |port| { echo "Running on port ${port}"} else { echo "Falling back to default"}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="background wait capture concurrency" > <h3> <span class="badge badge-feature">Feature</span> Background commands with <code>.wait</code> </h3> <p> Background execution uses a trailing <code>&</code>. Bound background executions capture stdout/stderr just like synchronous bindings, and <code>.wait</code> blocks until the work finishes. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">const job = (sleep 0.05; echo "done" &)echo "before wait"job.waitprintf "%s" "${job.stdout}"</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="structured flow control if else fn blocks" > <h3> <span class="badge badge-feature">Feature</span> Structured flow control </h3> <p> Functions, conditionals, and loops use modern syntax so scripts read like other languages instead of <code>then/fi</code> pairs. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">fn describe(count: Int) { if count > 1 { echo "plural" } else { echo "singular" }}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="iteration loops for while capture clause" > <h3> <span class="badge badge-feature">Feature</span> Native iteration constructs </h3> <p> <code>for</code> and <code>while</code> consume any iterator the runtime exposes, keeping capture bindings explicit. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">for (fruits, 0..) |fruit, idx| { echo "${idx}: ${fruit.upper()}"}
const reader = file.open("debug.log")while reader.lines() |line| { if line.starts_with("ERR") { echo line }}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="command vs expression separation pipes data" > <h3> <span class="badge badge-feature">Feature</span> Command vs. expression separation </h3> <p> Differentiate commands from pure expressions so data pipelines stay structured and word splitting disappears. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let files = ls ./src | lines()const uppercased = files.map(fn (path) => path.upper())</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="process handles stdout stderr background tee capture" > <h3> <span class="badge badge-feature">Feature</span> Processes as first-class values </h3> <p> Every command invocation yields a handle with stdout, stderr, status, and metadata instead of relying on global shell state. Bound command expressions keep that handle data available after <code>&&</code>, <code>||</code>, and <code>;</code> sequencing. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let sync_proc = git status --shortecho sync_proc.stdoutecho sync_proc.status.exit_code
const combined = printf "hello\n" && printf "warning\n" >&2echo combined.stdoutecho combined.stderr
const async_proc = (sleep 0.05; echo "done" &)async_proc.waitprintf "%s" "${async_proc.stdout}"</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="pipelines capture multiplexed io tee stream" > <h3> <span class="badge badge-feature">Feature</span> Capturing pipelines & multiplexed IO </h3> <p> Capture stdout/stderr independently, tee outputs to variables, and redirect streams without losing handle metadata. Explicit fd redirects such as <code>1>... </code>, <code>2>... </code>, and <code>1>&2</code> follow shell-style left-to-right ordering. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">make all | tee build.log 1>build_stdoutif build_stdout.status.exit_code != 0 { echo build_stdout.stdout}
echo "saved output" 1>"out.log"echo "saved error" 2>"err.log"echo "hello" 1>&2 2>"/dev/null"</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="error aware pipelines exit codes stage diagnostics" > <h3> <span class="badge badge-feature">Feature</span> Error-aware pipelines </h3> <p> Pipelines surface per-stage exit codes so you know exactly where a chain failed before deciding how to recover. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">let status = (build | tee build.log).statusif !status.ok { echo "Build failed at step ${status.failed_stage}" exit 1}</code></pre> </div> </article>
<article class="entry" data-searchable data-keywords="module system import reuse module file pub exports" > <h3> <span class="badge badge-feature">Feature</span> Module system and reuse </h3> <p> Import reusable helpers from other <code>.rn</code> files. Imported modules are resolved from the importing file, execute once per resolved path, expose <code>pub</code> declarations, and also retain execution-result fields on the imported value. </p> <div> <div class="example-label">Example</div> <pre><code class="language-rn">const math = import "util/math.rn"
echo math.piecho math.exit_code</code></pre> </div> </article>
</div> </section>
<p id="empty-state" class="empty-state is-hidden"> No matching types or features. Try another keyword. </p> </main> </div>
<script> const searchInput = document.getElementById("search"); const cards = Array.from(document.querySelectorAll("[data-searchable]")); const emptyState = document.getElementById("empty-state"); const sidebarTypes = document.getElementById("sidebar-types"); const sidebarFeatures = document.getElementById("sidebar-features");
function slugify(value) { return value .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, ""); }
function buildSidebar(sectionId, listEl) { if (!listEl) return; const sectionElement = document.getElementById(sectionId); if (!sectionElement) return;
const fragment = document.createDocumentFragment(); sectionElement.querySelectorAll("article.entry").forEach((card) => { const heading = card.querySelector("h3"); if (!heading) return;
const label = heading.textContent .trim() .replace(/^(Type|Feature)\s+/, ""); const baseSlug = slugify(label); let slug = baseSlug || `card-${Math.random().toString(36).slice(2)}`; let suffix = 1;
while (document.getElementById(slug) && card.id !== slug) { slug = `${baseSlug}-${suffix++}`; }
if (!card.id) { card.id = slug; }
const li = document.createElement("li"); const link = document.createElement("a"); link.href = `#${card.id}`; link.textContent = label; li.appendChild(link); fragment.appendChild(li); });
listEl.innerHTML = ""; listEl.appendChild(fragment); }
function applyFilter(query) { let visibleCount = 0; cards.forEach((card) => { const haystack = ( card.dataset.keywords + " " + card.textContent ).toLowerCase(); const match = haystack.includes(query); card.classList.toggle("is-hidden", query && !match); if (!query || match) { visibleCount++; } }); emptyState.classList.toggle("is-hidden", visibleCount !== 0); }
searchInput.addEventListener("input", (event) => { const query = event.target.value.trim().toLowerCase(); applyFilter(query); });
buildSidebar("types", sidebarTypes); buildSidebar("features", sidebarFeatures); applyFilter(""); </script> </body></html>