Ridgun
The language itself: a compiler that compiles itself, the stable project tool, the specification and the book
| 1 | # Ridgun — Platform Abstraction Layer (PAL) & System Libraries (`posix.rglib`, `io.rglib`) Specification |
| 2 | |
| 3 | This specification defines the contract between the **Ridgun Compiler (`ridgunc`)** and the **Ridgun System Libraries (`posix.rglib`, `io.rglib`, `unix-tools`)** across all supported and planned OS targets (`Linux`, `MacOS`, `Windows`) and CPU architectures (`X86_64`, `Arm64`). |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## 1. Architecture Overview: 2-Layer PAL + Target Sum ADTs |
| 8 | |
| 9 | Ridgun avoids exposing raw Linux-only syscall numbers or OS-specific bitflags in portable libraries by splitting system interaction into two compiler layers plus strongly typed target ADTs: |
| 10 | |
| 11 | 1. **Pre-Declared Target Platform Sum ADTs (`sys os => SysOs`, `sys arch => SysArch`)**: |
| 12 | - Rather than returning untyped `String`s (which are prone to typos and cannot be checked by the typechecker), `ridgunc` pre-declares two extensible Sum ADTs: |
| 13 | ```ridgun |
| 14 | type SysOs = Linux | MacOS | Windows |
| 15 | type SysArch = X86_64 | Arm64 |
| 16 | ``` |
| 17 | - `sys os => SysOs` and `sys arch => SysArch` are resolved by the backend at compile time into zero-overhead `NLoadAdt0` constants (`Linux` / `X86_64` on `x86_64-linux`, `Linux` / `Arm64` on `arm64-linux`). |
| 18 | - Libraries can dispatch cleanly by **tabular pattern matching** on `SysOs` / `SysArch`, compare with `==` (`(sys os) == Linux`), or extend them for custom bare-metal/BSD targets via `expand type SysOs = FreeBSD | BareMetal` (`FR-COMP-005`). |
| 19 | |
| 20 | 2. **Layer 1 — Cross-Platform PAL Primitives (`sys open mode`, `sys open`, `sys close`, `sys read fd`, `sys write fd`, `sys poll`, `sys terminate`, `sys kill`, `sys call`)**: |
| 21 | - All flag masks and poll event masks passed to Layer 1 primitives use **Ridgun PAL Normalized Constants**. |
| 22 | - Each backend (`x86_64.rg` ELF64, `arm64.rg` ELF64, and future `Mach-O` macOS / `PE64` Windows backends) translates those normalized constants into the native OS kernel ABI (`openat`/`poll`/`kill` on Linux, `open`/`poll`/`kill` on macOS, `CreateFileW`/`WaitForSingleObject`/`WSAPoll`/`TerminateProcess` on Windows). |
| 23 | - Both natural-language space-separated syntax (`sys open mode path "r"`) and underscore-separated syntax (`sys open mode path "r"`) are accepted identically. |
| 24 | |
| 25 | 3. **Layer 2 — Declarative Execution Quotas (`with timeout <duration> do`)**: |
| 26 | - Integer literals support duration suffixes: `_ms` (`* 1`), `_s` (`* 1000`), and `_min` (`* 60000`). |
| 27 | - `with timeout 500_ms do <body>` (and combined `with region 64_KB, timeout 500_ms do <body>`) pushes a lexical deadline (`sys time ms + duration_ms`) onto the thread-local PAL deadline stack, clamping to any stricter outer deadline (`min(outer, new)`) and restoring the outer deadline on exit. |
| 28 | - Inside a `with timeout` block, `sys deadline ms => Int` returns the active epoch deadline (`0` if unbounded), `sys remaining ms => Int` returns the remaining milliseconds (`-1` if unbounded, `0` if expired), and `sys poll fd events -1` automatically clamps its wait time to `sys remaining ms`. |
| 29 | |
| 30 | --- |
| 31 | |
| 32 | ## 2. Compiler Built-In PAL Reference (`FR-COMP-007`) |
| 33 | |
| 34 | ### 2.1 Target Platform Introspection |
| 35 | |
| 36 | | Primitive | Signature | Description | |
| 37 | |---|---|---| |
| 38 | | `sys os` | `=> SysOs` | Compile-time target operating system (`Linux \| MacOS \| Windows`). | |
| 39 | | `sys arch` | `=> SysArch` | Compile-time target CPU architecture (`X86_64 \| Arm64`). | |
| 40 | |
| 41 | Example of idiomatic multi-OS dispatch in a `.rglib` library without `if/else` chains: |
| 42 | |
| 43 | ```ridgun |
| 44 | fn :: path separator SysOs => String |
| 45 | path separator Linux = "/" |
| 46 | path separator MacOS = "/" |
| 47 | path separator Windows = "\\" |
| 48 | |
| 49 | fn :: null device => String |
| 50 | null device = null device for (sys os) |
| 51 | |
| 52 | fn :: null device for SysOs => String |
| 53 | null device for Windows = "NUL" |
| 54 | null device for _ = "/dev/null" |
| 55 | ``` |
| 56 | |
| 57 | ### 2.2 Descriptor / Handle I/O & Readiness Polling |
| 58 | |
| 59 | | Primitive | Signature | Normalized PAL Contract | |
| 60 | |---|---|---| |
| 61 | | `sys open mode` | `String String => Int` | Opens `path` with portable mode string `"r"`, `"w"`, `"a"`, `"rw"`, `"w+"`, or `"a+"` (default permission `0644` / `420` when creating). Returns descriptor/handle `>= 0` or negative error code (`-errno`). | |
| 62 | | `sys open` | `String Int Int => Int` | Opens `path` with Ridgun PAL normalized `flags` (`0` = read-only, `1` = write-only, `2` = read-write, `64` = create, `512` = truncate, `1024` = append, `2048` = non-blocking) and permission `mode` (e.g. `420` for `0644`). | |
| 63 | | `sys close` | `Int => Int` | Closes `fd`/handle. Returns `0` on success or negative `-errno`. | |
| 64 | | `sys read fd` | `Int Int => String` | Reads up to `max_bytes` from `fd`/handle (`0` = stdin, `1` = stdout, `2` = stderr, or opened file/pipe/FIFO). Returns `""` on EOF (`0` bytes) or error. | |
| 65 | | `sys write fd` | `Int String => Int` | Writes `data` to `fd`/handle. Returns bytes written (`>= 0`) or negative `-errno`. | |
| 66 | | `sys poll` | `Int Int Int => Int` | Polls `fd` for normalized `events` (`1` = `POLLIN` readable, `4` = `POLLOUT` writable, `8` = `POLLERR`, `16` = `POLLHUP`) with `timeout_ms` (`-1` = infinite or clamped to `sys remaining ms` when inside `with timeout`, `0` = non-blocking poll, `> 0` = max wait in ms). Returns `> 0` ready event bitmask, `0` on timeout, or `< 0` on `-errno`. | |
| 67 | |
| 68 | ### 2.3 Clocks, Deadlines, Signals & Low-Level Escape Hatch |
| 69 | |
| 70 | | Primitive | Signature | Normalized PAL Contract | |
| 71 | |---|---|---| |
| 72 | | `sys time ms` | `=> Int` | Monotonic millisecond clock (`CLOCK_MONOTONIC`). | |
| 73 | | `sys sleep ms` | `Int => ()` | Sleeps for `ms` milliseconds (`nanosleep` / `Sleep`). | |
| 74 | | `sys deadline ms` | `=> Int` | Active lexical deadline in monotonic ms (`0` when outside `with timeout`). | |
| 75 | | `sys remaining ms` | `=> Int` | Milliseconds remaining until active lexical deadline (`-1` when outside `with timeout`, `0` when expired). | |
| 76 | | `sys getpid` | `=> Int` | Current process ID (`> 0`). | |
| 77 | | `sys terminate` | `Int Bool => Int` | Cross-platform process termination: `force = False` sends graceful termination (`SIGTERM` on POSIX), `force = True` forces immediate termination (`SIGKILL` on POSIX, `TerminateProcess` on Windows). Returns `0` or `-errno`. | |
| 78 | | `sys kill` | `Int Int => Int` | Sends normalized signal (`0` = existence probe, `2` = `SIGINT`, `9` = `SIGKILL`, `15` = `SIGTERM`) to `pid` (or process group when `pid < 0`). Returns `0` or `-errno`. | |
| 79 | | `sys pipe` | `=> Int Int` | Creates an OS pipe (`__NR_pipe = 22`) and returns `(read_fd, write_fd)` (or `(-1, -1)` on error). | |
| 80 | | `sys waitpid` | `Int Int => Int Int` | Waits for child process `pid` with `options` (`0` = blocking, `1` = `WNOHANG`) via `__NR_wait4 = 61` and returns `(ret_pid, exit_status)` decoded via `WIFEXITED`/`WEXITSTATUS` (`0..255`) and `WIFSIGNALED` (`128 + WTERMSIG`). | |
| 81 | | `sys call` | `Int Int... => Int` | Low-level target-ABI raw syscall (`nr` plus `0..6` arguments `a1..a6`) for OS-specific drivers. Guard with `sys os` / `sys arch` when used. | |
| 82 | |
| 83 | --- |
| 84 | |
| 85 | ## 3. Recommended `.rglib` Library Surface (`io.rglib` & `posix.rglib`) |
| 86 | |
| 87 | ### 3.1 `io.rglib` — High-Level Timeout-Aware I/O Wrappers |
| 88 | |
| 89 | ```ridgun |
| 90 | type IoResult = IoOk String | IoTimeout | IoEof | IoError Int |
| 91 | |
| 92 | fn :: io read with timeout String Int => IoResult |
| 93 | io read with timeout path wait_ms = |
| 94 | with timeout wait_ms do |
| 95 | -- Open non-blocking (0 + 2048 = O_RDONLY | O_NONBLOCK) so FIFO/device open never hangs |
| 96 | fd = sys open path 2048 0 |
| 97 | io read poll step fd |
| 98 | |
| 99 | fn :: io read poll step Int => IoResult |
| 100 | io read poll step fd |
| 101 | | fd < 0 = IoError fd |
| 102 | | otherwise = |
| 103 | -- timeout_ms = -1 automatically uses `sys remaining ms` from the active `with timeout` block |
| 104 | ev = sys poll fd 1 (0 - 1) |
| 105 | io read after poll fd ev |
| 106 | |
| 107 | fn :: io read after poll Int Int => IoResult |
| 108 | io read after poll fd ev |
| 109 | | ev == 0 = |
| 110 | sys close fd -> () |
| 111 | IoTimeout |
| 112 | | ev < 0 = |
| 113 | sys close fd -> () |
| 114 | IoError ev |
| 115 | | otherwise = |
| 116 | chunk = sys read fd fd 65536 |
| 117 | sys close fd -> () |
| 118 | io wrap chunk chunk |
| 119 | |
| 120 | fn :: io wrap chunk String => IoResult |
| 121 | io wrap chunk "" = IoEof |
| 122 | io wrap chunk s = IoOk s |
| 123 | ``` |
| 124 | |
| 125 | ### 3.2 `posix.rglib` — Symbolic Constants & Process Helpers |
| 126 | |
| 127 | ```ridgun |
| 128 | -- Normalized PAL Open Flags |
| 129 | fn :: o rdonly => Int |
| 130 | o rdonly = 0 |
| 131 | |
| 132 | fn :: o wronly => Int |
| 133 | o wronly = 1 |
| 134 | |
| 135 | fn :: o rdwr => Int |
| 136 | o rdwr = 2 |
| 137 | |
| 138 | fn :: o creat => Int |
| 139 | o creat = 64 |
| 140 | |
| 141 | fn :: o trunc => Int |
| 142 | o trunc = 512 |
| 143 | |
| 144 | fn :: o append => Int |
| 145 | o append = 1024 |
| 146 | |
| 147 | fn :: o nonblock => Int |
| 148 | o nonblock = 2048 |
| 149 | |
| 150 | -- Normalized PAL Poll Events |
| 151 | fn :: poll in => Int |
| 152 | poll in = 1 |
| 153 | |
| 154 | fn :: poll out => Int |
| 155 | poll out = 4 |
| 156 | |
| 157 | fn :: poll err => Int |
| 158 | poll err = 8 |
| 159 | |
| 160 | fn :: poll hup => Int |
| 161 | poll hup = 16 |
| 162 | |
| 163 | -- Normalized Signals |
| 164 | fn :: sig int => Int |
| 165 | sig int = 2 |
| 166 | |
| 167 | fn :: sig kill => Int |
| 168 | sig kill = 9 |
| 169 | |
| 170 | fn :: sig term => Int |
| 171 | sig term = 15 |
| 172 | |
| 173 | fn :: process exists Int => Bool |
| 174 | process exists pid = (sys kill pid 0) == 0 |
| 175 | ``` |
| 176 | |
| 177 | --- |
| 178 | |
| 179 | ## 4. Building & Distributing `.rglib` and `.so` Libraries |
| 180 | |
| 181 | Compile any library module without requiring `main`: |
| 182 | |
| 183 | ```bash |
| 184 | # Emit portable Ridgun library bundle (.rglib) |
| 185 | ./bin/ridgunc build --lib src/main/ridgun/lib/io.rg -o target/io.rglib |
| 186 | |
| 187 | # Emit native ELF64 shared object (.so) |
| 188 | ./bin/ridgunc build --so src/main/ridgun/lib/io.rg -o target/libio.so |
| 189 | |
| 190 | # Or via stable in a library project (`stable new --lib io`): |
| 191 | ./bin/stable built lib |
| 192 | ``` |