-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
563 lines (498 loc) · 25.9 KB
/
index.html
File metadata and controls
563 lines (498 loc) · 25.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Skunk Language Reference</title>
<meta name="description" content="Skunk Language Reference">
<link rel="stylesheet" href="styles.css">
<script defer src="app.js"></script>
</head>
<body>
<div class="site-shell">
<aside class="sidebar">
<div class="brand">
<p class="eyebrow">Experimental Language Docs</p>
<h1>Skunk</h1>
<p class="brand-subtitle">Language Reference</p>
</div>
<label class="search-label" for="nav-filter">Jump to a section</label>
<input id="nav-filter" class="nav-filter" type="search" placeholder="Filter sections">
<nav id="doc-nav" class="doc-nav" aria-label="Reference navigation">
<a href="#overview">Overview</a>
<a href="#status">Status</a>
<a href="#compiler-notebook">Compiler Notebook</a>
<a href="#toolchain">Toolchain</a>
<a href="#programs-and-modules">Programs and Modules</a>
<a href="#types">Types</a>
<a href="#bindings-and-const">Bindings and Const</a>
<a href="#functions-and-control-flow">Functions and Control Flow</a>
<a href="#arrays-and-slices">Arrays and Slices</a>
<a href="#structs-and-methods">Structs and Attach</a>
<a href="#pointers-allocators-and-arenas">Pointers, Allocators, and Arenas</a>
<a href="#unsafe-memory">Unsafe Memory</a>
<a href="#windowed-2d">Windowed 2D</a>
<a href="#generics">Generics</a>
<a href="#enums-and-match">Enums and Match</a>
<a href="#traits-and-impls">Traits, Conform, Shapes</a>
<a href="#patterns-and-destructuring">Patterns and Destructuring</a>
<a href="#current-limitations">Current Limitations</a>
<a href="#design-notes">Design Notes</a>
</nav>
<div class="sidebar-note">
<p>Human-designed.</p>
<p>AI-implemented.</p>
<p>Not for critical software.</p>
</div>
</aside>
<main class="content">
<header id="overview" class="hero">
<span class="badge">LLVM-first</span>
<h1>Skunk Language Reference</h1>
<p class="lead">
Skunk is a human-designed, AI-implemented experimental programming language.
This reference covers the implemented language surface in the current compiler/runtime.
</p>
<div class="callout-grid">
<div class="callout">
<h2>Project Shape</h2>
<p>Skunk is a language-design project and compiler playground targeting LLVM.</p>
</div>
<div class="callout warning">
<h2>Safety Note</h2>
<p>Skunk is experimental and should not be used for critical, safety-sensitive, or high-reliability software.</p>
</div>
</div>
</header>
<section id="status">
<h2>Status</h2>
<p>The primary execution path is native compilation through LLVM and <code>clang</code>. The repository still contains legacy interpreter code, but the compiler/runtime path is the main focus.</p>
<ul>
<li>Source-level syntax is defined in <code>src/grammar.pest</code>.</li>
<li>Implemented behavior is backed by parser, type-checker, source-loader, and compiler tests.</li>
<li>This page documents the currently implemented language surface rather than future ideas.</li>
</ul>
</section>
<section id="compiler-notebook">
<h2>Compiler Notebook</h2>
<p>If you are new to compilers or new to LLVM, the repository now includes a slower, beginner-oriented guide to how Skunk is built.</p>
<ul>
<li><a href="compiler-booklet.html">Compiler booklet</a>: print-friendly HTML guide with diagrams and a worked example traced through the pipeline.</li>
<li><a href="compiler-notebook.md">Compiler notebook, Part 1</a>: high-level introduction to the architecture and reading order.</li>
<li><a href="compiler-notebook-part2.md">Compiler notebook, Part 2</a>: one small Skunk program followed through parsing, checking, layouts, LLVM lowering, and native build.</li>
<li><a href="compiler-notebook-part3.md">Compiler notebook, Part 3</a>: practical guide to extending Skunk feature by feature.</li>
</ul>
</section>
<section id="toolchain">
<h2>Toolchain</h2>
<p>Build Skunk with Rust, then compile a Skunk entry file into a native executable.</p>
<pre><code class="language-bash">cargo build
cargo run -- compile path/to/main.skunk ./out
./out</code></pre>
<p>The legacy interpreter path still exists while the codebase transitions further toward compiler-only execution.</p>
<pre><code class="language-bash">cargo run -- path/to/main.skunk</code></pre>
</section>
<section id="programs-and-modules">
<h2>Programs and Modules</h2>
<p>Skunk supports multi-file programs through <code>module</code>, <code>import</code>, and <code>export</code>.</p>
<pre><code class="language-skunk">module app.math;
function helper(n: int): int {
return n + 1;
}
export function inc(n: int): int {
return helper(n);
}</code></pre>
<pre><code class="language-skunk">import app.math;
function main(): void {
print(inc(41));
}</code></pre>
<ul>
<li><code>import app.math;</code> resolves to <code>app/math.skunk</code> relative to the entry file directory.</li>
<li>Imported files must declare the matching <code>module</code> name.</li>
<li>If a module uses <code>export</code>, only exported top-level declarations are visible to importers.</li>
<li>If a module uses no <code>export</code>, current behavior stays all-public for compatibility.</li>
</ul>
</section>
<section id="types">
<h2>Types</h2>
<p>Skunk currently supports primitive types, fixed arrays, slices, safe references, raw pointers, function types, structs, enums, and generic instantiations.</p>
<table class="reference-table">
<thead>
<tr>
<th>Form</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>byte</code>, <code>short</code>, <code>int</code>, <code>long</code></td>
<td>Signed integer primitives</td>
</tr>
<tr>
<td><code>float</code>, <code>double</code></td>
<td>Floating-point primitives</td>
</tr>
<tr>
<td><code>boolean</code>, <code>char</code>, <code>string</code>, <code>void</code></td>
<td>Core built-in types</td>
</tr>
<tr>
<td><code>[N]T</code></td>
<td>Fixed-size value array</td>
</tr>
<tr>
<td><code>[]T</code></td>
<td>Slice view over contiguous elements</td>
</tr>
<tr>
<td><code>&T</code>, <code>&mut T</code></td>
<td>Safe shared or mutable reference to one value</td>
</tr>
<tr>
<td><code>*T</code></td>
<td>Raw pointer to one value for unsafe memory operations</td>
</tr>
<tr>
<td><code>*const T</code>, <code>[]const T</code></td>
<td>Read-only pointer or slice view</td>
</tr>
<tr>
<td><code>(A, B) -> C</code></td>
<td>Function type</td>
</tr>
<tr>
<td><code>Box[int]</code></td>
<td>Generic type instantiation</td>
</tr>
</tbody>
</table>
<p><code>Allocator</code> and <code>Arena</code> are built-in runtime types used for explicit memory management.</p>
</section>
<section id="bindings-and-const">
<h2>Bindings and Const</h2>
<p>Skunk distinguishes between const bindings and const views.</p>
<pre><code class="language-skunk">const answer: int = 42;
function copy_into(const dst: []int, src: []const int): void {
for (i: int = 0; i < src.len; i = i + 1) {
dst[i] = src[i];
}
}</code></pre>
<ul>
<li><code>const name: T</code> makes the binding non-reassignable.</li>
<li><code>[]const T</code> and <code>*const T</code> make the viewed elements or pointee read-only.</li>
<li><code>[]T</code> is assignable to <code>[]const T</code>, but not the other way around.</li>
<li><code>const dst: []int</code> still allows <code>dst[i] = ...</code> because the binding is const, not the slice contents.</li>
</ul>
</section>
<section id="functions-and-control-flow">
<h2>Functions and Control Flow</h2>
<p>Skunk supports named functions, lambdas, closures, <code>if</code>, <code>for</code>, <code>return</code>, and block scoping.</p>
<pre><code class="language-skunk">function add(a: int, b: int): int {
return a + b;
}
function main(): void {
total: int = add(5, 7);
if (total > 10) {
print(total);
}
counter: () -> int = function(): int {
total = total + 1;
return total;
};
print(counter());
}</code></pre>
<p>Closures can capture and mutate surrounding locals. Function values can be stored, returned, and passed as arguments.</p>
</section>
<section id="arrays-and-slices">
<h2>Arrays and Slices</h2>
<p>Fixed arrays use value semantics. Slices are views over contiguous storage.</p>
<pre><code class="language-skunk">a: [4]int;
b: [4]int = [4]int::fill(7);
c: [4]int = [1, 2, 3, 4];
mid: []const int = c[1:3];
print(a[0]);
print(b.len);
print(mid[0]);</code></pre>
<ul>
<li><code>[N]T</code> without an initializer is zero-initialized.</li>
<li><code>[N]T::fill(value)</code> fills every element with the given value.</li>
<li>Slices support indexing, <code>.len</code>, and range slicing with omitted bounds.</li>
<li>Fixed arrays can be passed and returned by value.</li>
</ul>
</section>
<section id="structs-and-methods">
<h2>Structs and Attached Behavior</h2>
<p>Structs are data-only product types. Behavior lives in separate <code>attach</code> blocks.</p>
<pre><code class="language-skunk">struct Counter {
const seed: int;
value: int;
}
attach Counter {
function new(seed: int, value: int): Counter {
return Counter { seed: seed, value: value };
}
function bump(mut self): void {
self.value = self.value + 1;
}
function get(self): int {
return self.value;
}
}
function main(): void {
counter: Counter = Counter::new(1, 4);
counter.bump();
print(counter.seed);
print(counter.get());
}</code></pre>
<p>Receiver mutability is explicit:</p>
<ul>
<li><code>attach Type { ... }</code> adds inherent methods to a type without declaring trait conformance.</li>
<li>Attached functions without <code>self</code> are called with <code>Type::name(...)</code> and work well for constructors and factories.</li>
<li>Struct fields may be declared <code>const</code>; they may be initialized but not reassigned later.</li>
<li><code>self</code> means the method may read but may not mutate receiver state.</li>
<li><code>mut self</code> means the method may mutate receiver state.</li>
<li><code>*const T</code> may call only read-only <code>self</code> methods.</li>
</ul>
</section>
<section id="pointers-allocators-and-arenas">
<h2>Pointers, Allocators, and Arenas</h2>
<p>Skunk uses explicit allocation. Plain values use value semantics; safe borrows use <code>&T</code> and <code>&mut T</code>; allocator-backed single objects use <code>*T</code>; allocator-backed buffers use <code>[]T</code>.</p>
<pre><code class="language-skunk">struct Point {
x: int;
y: int;
}
function make_point(alloc: Allocator): *Point {
point: *Point = Point::create(alloc);
point.x = 3;
point.y = 4;
return point;
}
function main(): void {
system_alloc: Allocator = System::allocator();
arena: Arena = Arena::init(system_alloc);
arena_alloc: Allocator = arena.allocator();
point: *Point = make_point(arena_alloc);
values: []int = []int::alloc(arena_alloc, 8);
print(point.x + values.len);
arena.deinit();
}</code></pre>
<ul>
<li><code>System::allocator()</code> returns the system allocator handle.</li>
<li><code>T::create(alloc)</code> allocates one object and returns <code>*T</code>.</li>
<li><code>[]T::alloc(alloc, len)</code> allocates a slice buffer.</li>
<li><code>alloc.destroy(ptr)</code> releases a pointer allocation.</li>
<li><code>alloc.free(slice)</code> releases a slice allocation.</li>
<li><code>Arena::init(backing)</code>, <code>arena.allocator()</code>, <code>arena.reset()</code>, and <code>arena.deinit()</code> provide arena-style lifetime management.</li>
<li><code>&T</code> shares read-only access and <code>&mut T</code> grants checked mutable access without entering <code>unsafe</code>.</li>
<li>Field access like <code>point.x</code> and method calls like <code>point.bump()</code> still auto-deref ordinary typed pointers.</li>
</ul>
</section>
<section id="unsafe-memory">
<h2>Unsafe Memory</h2>
<p>Skunk now has a small unsafe memory layer for low-level pointer work. These operations must appear inside an <code>unsafe { ... }</code> block.</p>
<pre><code class="language-skunk">function main(): void {
value: int = 41;
other: int = 0;
unsafe {
ptr: *int = &value;
print(ptr.*);
ptr.* = 42;
bytes: [4]byte;
second: *byte = *byte::offset(&bytes[0], 1);
second.* = 9;
Memory::set(&bytes[0], 7, 4);
Memory::copy(*byte::cast(&other), *byte::cast(&value), int::size_of());
}
print(int::size_of());
print(int::align_of());
}</code></pre>
<ul>
<li><code>unsafe { ... }</code> enables low-level operations the compiler cannot verify as memory-safe.</li>
<li><code>&expr</code> and <code>&mut expr</code> create safe references by default.</li>
<li>When a raw pointer is explicitly expected, the same address-of syntax feeds unsafe pointer operations such as <code>*T::cast</code>, <code>*byte::offset</code>, and <code>Memory::copy</code>.</li>
<li><code>ptr.*</code> explicitly dereferences a pointer value.</li>
<li><code>T::size_of()</code> and <code>T::align_of()</code> are safe compile-time layout queries.</li>
<li><code>*T::cast(ptr)</code> reinterprets one pointer type as another pointer type.</li>
<li><code>*byte::offset(ptr, n)</code> performs byte-wise pointer offsetting.</li>
<li><code>Memory::copy(dst, src, count)</code> and <code>Memory::set(dst, value, count)</code> operate on raw bytes.</li>
</ul>
</section>
<section id="windowed-2d">
<h2>Windowed 2D</h2>
<p>Skunk now includes a small window/input/drawing runtime for simple 2D programs. The current implementation is macOS-first and is aimed at rectangle-based games and visual prototypes.</p>
<pre><code class="language-skunk">function main(): void {
window: Window = Window::create(800, 600, "Skunk");
for (; window.is_open(); ) {
window.poll();
if (Keyboard::is_down(window, 'q')) {
window.close();
}
window.clear(Color::rgb(8, 12, 24));
window.draw_rect(120.0, 140.0, 96.0, 64.0, Color::white());
window.present();
}
window.deinit();
}</code></pre>
<ul>
<li><code>Window::create(width, height, title)</code> creates a native window handle.</li>
<li><code>window.poll()</code> pumps OS events so keyboard and close state stay current.</li>
<li><code>window.is_open()</code>, <code>window.close()</code>, and <code>window.deinit()</code> control the window lifetime.</li>
<li><code>window.clear(color)</code> fills the framebuffer and <code>window.draw_rect(x, y, w, h, color)</code> draws clipped solid rectangles.</li>
<li><code>window.present()</code> shows the current frame and updates <code>window.delta_time()</code>.</li>
<li><code>Keyboard::is_down(window, 'w')</code> currently uses character keys; arrow keys and richer input enums can come later.</li>
<li><code>Color::rgb(r, g, b)</code>, <code>Color::rgba(r, g, b, a)</code>, and constants like <code>Color::white()</code> pack colors for drawing.</li>
<li>The repository now includes <code>examples/pong.skunk</code> as a complete example built on this API.</li>
</ul>
</section>
<section id="generics">
<h2>Generics</h2>
<p>Skunk supports generic structs, generic functions, and generic enums through monomorphization.</p>
<pre><code class="language-skunk">struct Box[T] {
value: T;
}
function wrap[T](value: T): Box[T] {
return Box[T] { value: value };
}</code></pre>
<ul>
<li>Nested instantiations such as <code>Box[Box[int]]</code> are supported.</li>
<li>Function type argument inference works from call arguments in common cases.</li>
<li>Explicit function call type arguments are supported with forms like <code>id[int](42)</code>.</li>
</ul>
</section>
<section id="enums-and-match">
<h2>Enums and Match</h2>
<p>Skunk supports generic enums with unit variants and tuple-style payload variants, plus exhaustive enum-focused <code>match</code>.</p>
<pre><code class="language-skunk">enum Option[T] {
None;
Some(T);
}
function unwrap(value: Option[int]): int {
match (value) {
case None: {
return 0;
}
case Some(v): {
return v;
}
}
}</code></pre>
<ul>
<li>Construct variants with forms like <code>Option[int]::None()</code> and <code>Option[int]::Some(7)</code>.</li>
<li>Variants may carry multiple payload values, such as <code>Pair(A, B)</code>.</li>
<li><code>match</code> is exhaustiveness-checked for enums.</li>
</ul>
</section>
<section id="traits-and-impls">
<h2>Traits, Conform, and Shapes</h2>
<p>Traits work both as generic constraints and as runtime interface values. Traits may extend other traits, and shapes provide reusable structural bounds.</p>
<pre><code class="language-skunk">trait Readable {
function value(self): int;
}
trait Writer: Readable {
function write(mut self, value: int): int;
function write_twice(mut self, value: int): int {
self.write(value);
return self.write(value);
}
}
trait Resettable {
function reset(mut self): void;
}
shape WriterLike {
function write(mut self, value: int): int;
}
struct Counter {
value: int;
}
conform Writer for Counter {
function value(self): int {
return self.value;
}
function write(mut self, value: int): int {
self.value = self.value + value;
return self.value;
}
}
conform Resettable for Counter {
function reset(mut self): void {
self.value = 0;
}
}
function use_counter[T: Writer + Resettable](counter: *T): int {
counter.reset();
return counter.write(41);
}
function save[T](value: T): T
where T: Writer + Resettable {
return value;
}
function use_writer_like[T: WriterLike](writer: *T): int {
return writer.write(5);
}</code></pre>
<pre><code class="language-skunk">function main(): void {
writer: Writer = Counter { value: 1 };
print(writer.write_twice(4));
}</code></pre>
<ul>
<li>Trait conformance is explicit through <code>conform Trait for Type { ... }</code>.</li>
<li>Traits may extend other traits with <code>trait Writer: Readable { ... }</code>; implementing the child trait also satisfies the parent traits.</li>
<li>Traits may provide default method bodies, and <code>conform</code> blocks only need to implement the required methods they want to customize.</li>
<li>Shapes provide structural bounds, for example <code>T: WriterLike</code>, without introducing a runtime trait value.</li>
<li>Conform targets may be concrete or generic, for example <code>conform[T] SizedThing for Box[T] { ... }</code>.</li>
<li>Generic bounds can stay inline with <code>function save[T: Writer](...)</code> or move into a <code>where</code> clause for longer signatures.</li>
<li>Bounds stack with <code>+</code>, for example <code>where T: Writer + Resettable</code>.</li>
<li>Trait names may be used as runtime types, such as <code>writer: Writer</code>.</li>
<li>Assigning an addressable concrete value to a trait value borrows its storage for dynamic dispatch; rvalues still box a runtime value with a vtable.</li>
</ul>
</section>
<section id="patterns-and-destructuring">
<h2>Patterns and Destructuring</h2>
<p>Skunk currently supports enum patterns in <code>match</code>, struct patterns in <code>match</code>, and standalone struct destructuring statements.</p>
<pre><code class="language-skunk">struct Point {
x: int;
y: int;
}
function sum(point: Point): int {
match (point) {
case Point { x, y }: {
return x + y;
}
}
}
function main(): void {
point: Point = Point { x: 3, y: 4 };
Point { x, y: py } = point;
print(sum(point));
print(x + py);
}</code></pre>
<ul>
<li>Struct field bindings may use aliases such as <code>y: py</code>.</li>
<li>Destructuring statements introduce local bindings in the current scope.</li>
<li>Struct pattern matching is exact-type and currently supports one case in V1.</li>
</ul>
</section>
<section id="current-limitations">
<h2>Current Limitations</h2>
<ul>
<li>No user-defined allocators yet. The current allocator and arena model is still runtime-provided.</li>
<li>The unsafe memory layer is intentionally small today: there are no raw pointer trait bounds, no arbitrary pointer arithmetic beyond <code>*byte::offset</code>, and no general <code>unsafe</code> standard library yet.</li>
<li>Struct <code>match</code> is intentionally narrow in this first pass.</li>
<li>Runtime trait values still use a simple V1 representation. Lvalues reuse their existing storage, while temporaries and other rvalues are boxed when converted to trait values.</li>
</ul>
</section>
<section id="design-notes">
<h2>Design Notes</h2>
<p>The language reference should stay focused on implemented behavior. For deeper design context, see the supporting notes in this repository.</p>
<ul>
<li><a href="compiler-booklet.html">Compiler booklet</a></li>
<li><a href="compiler-notebook.md">Compiler notebook, Part 1</a></li>
<li><a href="compiler-notebook-part2.md">Compiler notebook, Part 2</a></li>
<li><a href="compiler-notebook-part3.md">Compiler notebook, Part 3</a></li>
<li><a href="pointers-and-allocators.md">Pointer and allocator design</a></li>
<li><a href="language-development.md">Language development contract</a></li>
</ul>
</section>
</main>
</div>
</body>
</html>