srsh 0.7.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +18 -14
  3. data/README.md +351 -134
  4. data/bin/srsh +53 -1473
  5. data/docs/assets/slut.txt +4 -0
  6. data/docs/assets/srsh-mark.svg +12 -0
  7. data/docs/css/style.css +696 -0
  8. data/docs/index.html +703 -0
  9. data/docs/js/app.js +203 -0
  10. data/examples/bridge.rsh +8 -0
  11. data/examples/calculator.rsh +253 -0
  12. data/examples/defer.rsh +14 -0
  13. data/examples/hot.rsh +14 -0
  14. data/examples/meta.rsh +20 -0
  15. data/examples/modules/text.rsh +6 -0
  16. data/examples/modules.rsh +6 -0
  17. data/examples/paste.rsh +15 -0
  18. data/examples/plugin.rb +8 -0
  19. data/examples/power.rsh +65 -0
  20. data/examples/tour.rsh +38 -0
  21. data/ext/srsh_native/extconf.rb +3 -0
  22. data/ext/srsh_native/srsh_native.c +48 -0
  23. data/language-docs/LANGUAGE.md +670 -0
  24. data/language-docs/MIGRATION.md +44 -0
  25. data/language-docs/SECURITY.md +44 -0
  26. data/lib/srsh/app.rb +261 -0
  27. data/lib/srsh/builtins.rb +492 -0
  28. data/lib/srsh/editor.rb +530 -0
  29. data/lib/srsh/errors.rb +23 -0
  30. data/lib/srsh/history.rb +74 -0
  31. data/lib/srsh/language/evaluator.rb +1175 -0
  32. data/lib/srsh/language/lexer.rb +316 -0
  33. data/lib/srsh/language/parser.rb +997 -0
  34. data/lib/srsh/language/token.rb +5 -0
  35. data/lib/srsh/language/values.rb +392 -0
  36. data/lib/srsh/paths.rb +29 -0
  37. data/lib/srsh/plugins.rb +59 -0
  38. data/lib/srsh/process_identity.rb +38 -0
  39. data/lib/srsh/security.rb +38 -0
  40. data/lib/srsh/shell/executor.rb +1182 -0
  41. data/lib/srsh/shell/job.rb +101 -0
  42. data/lib/srsh/shell/lexer.rb +114 -0
  43. data/lib/srsh/shell/terminal.rb +26 -0
  44. data/lib/srsh/state.rb +136 -0
  45. data/lib/srsh/theme.rb +108 -0
  46. data/lib/srsh/version.rb +1 -2
  47. data/lib/srsh.rb +15 -0
  48. metadata +59 -11
data/docs/index.html ADDED
@@ -0,0 +1,703 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="description" content="SRSH is a Unix shell written in Ruby, with the RSH scripting language for structured shell programs.">
7
+ <title>SRSH 1.0: Simple Ruby Shell</title>
8
+ <link rel="icon" href="assets/srsh-mark.svg">
9
+ <link rel="stylesheet" href="css/style.css">
10
+ <script src="js/app.js" defer></script>
11
+ </head>
12
+ <body>
13
+ <a class="skip-link" href="#content">Skip to content</a>
14
+
15
+ <header class="site-header">
16
+ <div class="site-width masthead">
17
+ <a class="site-name" href="#/" aria-label="SRSH home">SRSH</a>
18
+ <div class="site-title">
19
+ <strong>Simple Ruby Shell</strong>
20
+ <span>Unix commands and the RSH language</span>
21
+ </div>
22
+ <div class="version-box">Version <span data-latest-version>1.0.0</span></div>
23
+ </div>
24
+ <nav class="main-nav" aria-label="Main navigation">
25
+ <div class="site-width">
26
+ <a href="#/" data-route="home">Home</a>
27
+ <a href="#/manual" data-route="manual">Language manual</a>
28
+ <a href="#/download" data-route="download">Download</a>
29
+ <a href="#/examples" data-route="examples">Examples</a>
30
+ <a href="#/project" data-route="project">Project</a>
31
+ </div>
32
+ </nav>
33
+ </header>
34
+
35
+ <div class="site-width status-line">
36
+ Current source: 1.0.0&nbsp;&nbsp;|&nbsp;&nbsp;Requires Ruby 3.2 or newer&nbsp;&nbsp;|&nbsp;&nbsp;Unix systems
37
+ </div>
38
+
39
+ <main id="content" class="site-width site-body">
40
+ <section class="page" data-page="home">
41
+ <div class="home-layout">
42
+ <article>
43
+ <h1>SRSH 1.0</h1>
44
+ <p class="lead">SRSH is a Unix shell written in Ruby. RSH is the language built into it.</p>
45
+ <p>
46
+ Ordinary commands keep ordinary shell syntax. When a script needs lists, maps, functions,
47
+ errors, workers, modules, or objects, it can use RSH without leaving the shell.
48
+ </p>
49
+
50
+ <div class="listing">
51
+ <div class="listing-title">A small RSH program</div>
52
+ <pre id="home-example"><code>branch := $(git branch --show-current)
53
+
54
+ files := glob("src/**/*.rb")
55
+ |&gt; reject(::path =&gt; contains(path, "/vendor/"))
56
+ |&gt; sort
57
+
58
+ ? files |&gt; len &gt; 20 =&gt; = "#{branch}: plenty of Ruby"</code></pre>
59
+ <button class="copy-button" type="button" data-copy-target="home-example">copy</button>
60
+ </div>
61
+
62
+ <h2>Two kinds of pipe</h2>
63
+ <p>SRSH keeps process pipelines and value pipelines separate.</p>
64
+ <table class="reference-table">
65
+ <thead>
66
+ <tr><th>Form</th><th>What moves</th></tr>
67
+ </thead>
68
+ <tbody>
69
+ <tr><td><code>command | command</code></td><td>Bytes between Unix processes</td></tr>
70
+ <tr><td><code>value |&gt; function</code></td><td>RSH values between functions</td></tr>
71
+ <tr><td><code>$(command)</code></td><td>Command output into an RSH string</td></tr>
72
+ <tr><td><code>cmd("git", "status")</code></td><td>Arguments kept as structured data</td></tr>
73
+ </tbody>
74
+ </table>
75
+
76
+ <h2>What is implemented</h2>
77
+ <div class="columns">
78
+ <ul>
79
+ <li>Unix pipelines, redirection, connectors, and background jobs</li>
80
+ <li>Local and environment bindings</li>
81
+ <li>Lists, maps, ranges, strings, numbers, booleans, and <code>void</code></li>
82
+ <li>Functions, lambdas, closures, and value pipelines</li>
83
+ <li>Pattern matching and safe access</li>
84
+ </ul>
85
+ <ul>
86
+ <li>Prototypes, traits, namespaces, and modules</li>
87
+ <li>Structured errors and deferred cleanup</li>
88
+ <li>Tasks, atoms, channels, thread pools, and process workers</li>
89
+ <li>Parsed code values and reflection</li>
90
+ <li>Direct C ABI calls with <code>bridge</code></li>
91
+ </ul>
92
+ </div>
93
+
94
+ <h2>Find a manual entry</h2>
95
+ <form class="doc-search" data-doc-search>
96
+ <label for="manual-search">Search this manual:</label>
97
+ <input id="manual-search" type="search" autocomplete="off" placeholder="try: tasks, match, bridge, glob">
98
+ <button type="submit">Search</button>
99
+ </form>
100
+ <div class="search-results" data-search-results hidden></div>
101
+ </article>
102
+
103
+ <aside class="home-side">
104
+ <section class="side-box download-box">
105
+ <h2>Download</h2>
106
+ <p>Release files are built from version tags by GitHub Actions.</p>
107
+ <a class="download-link" data-download="gem" href="https://github.com/RobertFlexx/RSH/releases/latest/download/srsh.gem">Download latest gem</a>
108
+ <a class="download-link secondary" data-download="source" href="https://github.com/RobertFlexx/RSH/releases/latest/download/srsh-source.tar.gz">Download source archive</a>
109
+ <p class="small"><a href="https://github.com/RobertFlexx/RSH/releases/latest">Release notes and checksums</a></p>
110
+ </section>
111
+
112
+ <section class="side-box">
113
+ <h2>Quick start</h2>
114
+ <pre><code>gem install ./srsh.gem
115
+ srsh</code></pre>
116
+ <p><a href="#/download">Installation details</a></p>
117
+ </section>
118
+
119
+ <section class="side-box">
120
+ <h2>Documents</h2>
121
+ <ul class="plain-links">
122
+ <li><a href="#/manual">Language manual</a></li>
123
+ <li><a href="#/examples">Example programs</a></li>
124
+ <li><a href="https://github.com/RobertFlexx/RSH/blob/master/language-docs/MIGRATION.md">Migration notes</a></li>
125
+ <li><a href="https://github.com/RobertFlexx/RSH/blob/master/language-docs/SECURITY.md">Security notes</a></li>
126
+ </ul>
127
+ </section>
128
+ </aside>
129
+ </div>
130
+ </section>
131
+
132
+ <section class="page" data-page="manual">
133
+ <div class="page-heading">
134
+ <h1>RSH language manual</h1>
135
+ <p>This manual describes the 1.0 source tree. Examples use the readable syntax unless the short form is the point.</p>
136
+ </div>
137
+
138
+ <div class="manual-layout">
139
+ <aside class="toc" aria-label="Manual contents">
140
+ <strong>Contents</strong>
141
+ <a href="#/manual#getting-started">1. Getting started</a>
142
+ <a href="#/manual#shell">2. Shell commands</a>
143
+ <a href="#/manual#values">3. Values</a>
144
+ <a href="#/manual#bindings">4. Bindings</a>
145
+ <a href="#/manual#operators">5. Operators</a>
146
+ <a href="#/manual#functions">6. Functions</a>
147
+ <a href="#/manual#control">7. Control flow</a>
148
+ <a href="#/manual#matching">8. Pattern matching</a>
149
+ <a href="#/manual#collections">9. Collections</a>
150
+ <a href="#/manual#objects">10. Prototypes</a>
151
+ <a href="#/manual#modules">11. Namespaces</a>
152
+ <a href="#/manual#errors">12. Errors and cleanup</a>
153
+ <a href="#/manual#concurrency">13. Concurrency</a>
154
+ <a href="#/manual#commands">14. Command values</a>
155
+ <a href="#/manual#bridge">15. C bridges</a>
156
+ <a href="#/manual#meta">16. Code and reflection</a>
157
+ <a href="#/manual#builtins">17. Function reference</a>
158
+ <a href="#/manual#configuration">18. Shell configuration</a>
159
+ </aside>
160
+
161
+ <article class="manual">
162
+ <section id="getting-started" data-doc-section data-title="Getting started">
163
+ <h2>1. Getting started</h2>
164
+ <p>Start the interactive shell with <code>srsh</code>, run a script by path, or evaluate a single expression.</p>
165
+ <div class="listing">
166
+ <pre id="start-code"><code>srsh
167
+ srsh script.rsh one two
168
+ srsh -e "[1,2,3] |&gt; sum"
169
+ srsh --check script.rsh
170
+ srsh -c "printf 'hello\n'"
171
+ srsh --norc</code></pre>
172
+ <button class="copy-button" type="button" data-copy-target="start-code">copy</button>
173
+ </div>
174
+ <p>
175
+ At the prompt, assignments, expressions, functions, and blocks are parsed as RSH. Other input is handled as a shell command.
176
+ The editor understands multiline blocks, collections, command continuations, and bracketed paste.
177
+ </p>
178
+ </section>
179
+
180
+ <section id="shell" data-doc-section data-title="Shell commands and jobs">
181
+ <h2>2. Shell commands and jobs</h2>
182
+ <p>These forms run commands through the shell executor:</p>
183
+ <div class="listing"><pre><code>cat *.log | grep ERROR | sort -u
184
+ make -j8 &amp;&amp; put built
185
+ generate &gt; output.txt 2&gt; errors.txt
186
+ long_job &amp;
187
+ jobs
188
+ fg %1</code></pre></div>
189
+ <p>
190
+ Implemented operators are <code>|</code>, <code>&amp;&amp;</code>, <code>||</code>, <code>;</code>, and background <code>&amp;</code>.
191
+ Redirections include <code>&lt;</code>, <code>&gt;</code>, <code>&gt;&gt;</code>, <code>2&gt;</code>, and <code>2&gt;&gt;</code>.
192
+ The shell supports process groups, <code>jobs</code>, <code>fg</code>, <code>bg</code>, <code>wait %N</code>, aliases, globbing,
193
+ tilde expansion, and nested command substitution.
194
+ </p>
195
+ <p>No-match globs stay literal. SRSH is not a Bash parser, so existing Bash scripts should still be run with Bash.</p>
196
+ </section>
197
+
198
+ <section id="values" data-doc-section data-title="Values and literals">
199
+ <h2>3. Values and literals</h2>
200
+ <div class="listing"><pre><code>42 -12 0xff 0b1010 0o755 1_000_000
201
+ 3.14159 2.5e6
202
+ yes no void
203
+
204
+ "hello #{name}"
205
+ 'no interpolation #{name}'
206
+ [[raw text #{name}]]
207
+
208
+ [1, 2, 3]
209
+ %[name: "srsh", ready: yes]
210
+ 1 .. 10
211
+ 0 ..&lt; 10</code></pre></div>
212
+ <p>
213
+ Double quoted strings interpolate full RSH expressions. Single quoted and raw strings do not.
214
+ Maps use <code>%[...]</code>. Ranges can include or exclude the upper bound.
215
+ </p>
216
+ <p>Member access uses <code>value.name</code> and indexed access uses <code>value[index]</code>. Safe forms return <code>void</code> when access cannot be completed:</p>
217
+ <div class="listing"><pre><code>host := cfg?.server?.host ?? "localhost"
218
+ first := cfg?.hosts?[0] ?? host</code></pre></div>
219
+ </section>
220
+
221
+ <section id="bindings" data-doc-section data-title="Bindings and assignment">
222
+ <h2>4. Bindings and assignment</h2>
223
+ <div class="listing"><pre><code>name := "Robert"
224
+ count := 4
225
+ count += 1
226
+ name ++= "!"
227
+
228
+ $EDITOR := "vim"
229
+ home := $HOME
230
+ first_arg := $1
231
+ last_status := $?</code></pre></div>
232
+ <p>
233
+ <code>:=</code> creates a local in the current lexical scope. Compound assignment updates the nearest existing local.
234
+ A leading <code>$</code> on assignment writes the process environment. Shell commands can expand locals, environment variables,
235
+ positional arguments, <code>$?</code>, and <code>$!</code>.
236
+ </p>
237
+ <p>Tasks receive isolated copies of ordinary lists and maps. Shared mutation must use a prototype object, atom, or channel.</p>
238
+ </section>
239
+
240
+ <section id="operators" data-doc-section data-title="Operators and value pipelines">
241
+ <h2>5. Operators and value pipelines</h2>
242
+ <p>Operator groups, from loose to tight, are approximately:</p>
243
+ <div class="listing"><pre><code>??
244
+ or ||
245
+ and &amp;&amp;
246
+ == != === !== =~ !~ in
247
+ &lt; &lt;= &gt; &gt;=
248
+ |&gt;
249
+ .. ..&lt;
250
+ + - ++
251
+ * / %
252
+ **</code></pre></div>
253
+ <p><code>++</code> concatenates strings. <code>+</code> is numeric unless its operands support a sensible arithmetic addition. Strict equality uses <code>===</code>.</p>
254
+ <p>The value pipeline inserts its left side as the first argument to the call on its right:</p>
255
+ <div class="listing"><pre><code>names := glob("src/**/*.rb")
256
+ |&gt; map(::path =&gt; basename(path))
257
+ |&gt; uniq
258
+ |&gt; sort</code></pre></div>
259
+ </section>
260
+
261
+ <section id="functions" data-doc-section data-title="Functions lambdas and tasks">
262
+ <h2>6. Functions, lambdas, and tasks</h2>
263
+ <div class="listing"><pre><code>fn scale(x, by := 2)
264
+ return x * by
265
+ end
266
+
267
+ fn square(x) =&gt; x * x
268
+
269
+ double := ::x =&gt; x * 2
270
+ sum_rest := ::(head, *tail) =&gt; tail |&gt; sum
271
+
272
+ task fetch(url)
273
+ return cmd("curl", "-fsS", url).check().out
274
+ end</code></pre></div>
275
+ <p>
276
+ Functions are first-class values and closures capture lexical values. Parameters may have defaults and one trailing rest parameter.
277
+ A <code>task</code> has the same parameter rules, but calling it starts work immediately and returns a task handle.
278
+ </p>
279
+ <p>Short named functions use <code>:: name(args) ... .::</code>. A caret is accepted as the short spelling of <code>return</code>.</p>
280
+ </section>
281
+
282
+ <section id="control" data-doc-section data-title="Conditionals loops and short forms">
283
+ <h2>7. Control flow</h2>
284
+ <div class="listing"><pre><code>if score &gt;= 90
285
+ emit "great"
286
+ else
287
+ emit "keep going"
288
+ end
289
+
290
+ each users -&gt; user
291
+ = user.name
292
+ end
293
+
294
+ while pending
295
+ work()
296
+ end</code></pre></div>
297
+ <p>Integers are iterable, so <code>each 3 -&gt; i</code> visits 0, 1, and 2. Maps yield key and value pairs. Lists and pairs can be destructured.</p>
298
+ <div class="listing"><pre><code>head, second, *rest := [10, 20, 30, 40]
299
+
300
+ ? ready =&gt; emit "go"
301
+ @ users -&gt; user =&gt; = user.name
302
+ @? pending
303
+ work()
304
+ .@</code></pre></div>
305
+ <p>The short block markers are <code>? ... .?</code> for conditions and <code>@ ... .@</code> for loops. Use <code>break</code> or <code>^!</code>, and <code>continue</code> or <code>^&gt;</code>.</p>
306
+ <p>The original <code>times N ... end</code> loop and <code>fn name arg ... end</code> function spelling remain available for older scripts.</p>
307
+ </section>
308
+
309
+ <section id="matching" data-doc-section data-title="Pattern matching">
310
+ <h2>8. Pattern matching</h2>
311
+ <div class="listing"><pre><code>match status
312
+ | 200..299 =&gt; = "ok"
313
+ | [401, 403] =&gt; = "auth"
314
+ | ? it &gt;= 500 =&gt; = "server"
315
+ | _ =&gt; = "other"
316
+ end</code></pre></div>
317
+ <p>
318
+ Patterns may be values, ranges, lists, partial maps, prototype references, or guard expressions.
319
+ The matched value is available as <code>it</code> in a guard. The short form begins with <code>??</code> and ends with <code>.??</code>.
320
+ </p>
321
+ </section>
322
+
323
+ <section id="collections" data-doc-section data-title="Collection functions">
324
+ <h2>9. Collection functions</h2>
325
+ <p>The collection toolbox works in call form, pipeline form, and usually method form.</p>
326
+ <div class="word-list"><code>map filter reject fold find any all count sum each sort uniq flat zip enumerate take drop chunk group tap partial compose</code></div>
327
+ <div class="listing"><pre><code>total := [1,2,3,4,5,6]
328
+ .filter(::x =&gt; x % 2 == 0)
329
+ .map(::x =&gt; x ** 2)
330
+ .sum()
331
+
332
+ by_ext := files |&gt; group(::path =&gt; ext(path))</code></pre></div>
333
+ <p>File, path, string, and JSON helpers include:</p>
334
+ <div class="word-list"><code>readfile writefile appendfile exists file dir glob stat mkdirp rmfile cpfile mvfile basename dirname ext json json_dump lines words replace upper lower trim split join shellquote</code></div>
335
+ </section>
336
+
337
+ <section id="objects" data-doc-section data-title="Prototypes traits and objects">
338
+ <h2>10. Prototypes, traits, and objects</h2>
339
+ <p>RSH uses prototypes with composable traits instead of class inheritance.</p>
340
+ <div class="listing"><pre><code>trait Printable
341
+ fn show() =&gt; "#{self.name}=#{self.value}"
342
+ end
343
+
344
+ proto Counter(name, start := 0) with Printable
345
+ slot name := name
346
+ slot value := start
347
+
348
+ fn inc(by := 1)
349
+ self.value += by
350
+ return self
351
+ end
352
+ end
353
+
354
+ counter := Counter("requests", 10)
355
+ counter.inc()</code></pre></div>
356
+ <p>
357
+ Slot compound updates are synchronized. Reflection helpers include <code>fields()</code>, <code>methods()</code>,
358
+ <code>protoof()</code>, <code>is()</code>, and <code>clone()</code>.
359
+ </p>
360
+ </section>
361
+
362
+ <section id="modules" data-doc-section data-title="Namespaces modules and use">
363
+ <h2>11. Namespaces and modules</h2>
364
+ <div class="listing"><pre><code>space build
365
+ root := "out"
366
+ fn artifact(name) =&gt; root ++ "/" ++ name
367
+ end
368
+
369
+ use "./lib/net.rsh" as net
370
+
371
+ = build.artifact("app")
372
+ = net.fetch(url)</code></pre></div>
373
+ <p>
374
+ A <code>space</code> may contain bindings, functions, tasks, prototypes, traits, nested spaces, bridges, and code declarations.
375
+ <code>use</code> runs a file inside its own namespace. Relative paths are resolved against the importing script, and import cycles are rejected.
376
+ </p>
377
+ </section>
378
+
379
+ <section id="errors" data-doc-section data-title="Structured errors defer and attempt">
380
+ <h2>12. Errors and cleanup</h2>
381
+ <div class="listing"><pre><code>try
382
+ cfg := json(readfile("config.json"))
383
+ catch err
384
+ = err.message
385
+ cfg := %[]
386
+ finally
387
+ audit("attempted")
388
+ end
389
+
390
+ defer rmfile(tmp)
391
+ result := attempt(:: =&gt; risky())</code></pre></div>
392
+ <p>
393
+ Caught errors provide at least <code>.type</code> and <code>.message</code>. Deferred calls and blocks run last-in, first-out when
394
+ the current script or function scope exits, including on return and error. <code>fail()</code> raises an error and <code>assert()</code> checks a condition.
395
+ </p>
396
+ </section>
397
+
398
+ <section id="concurrency" data-doc-section data-title="Tasks atoms channels parallel and pmap">
399
+ <h2>13. Concurrency</h2>
400
+ <div class="listing"><pre><code>jobs := urls |&gt; map(fetch)
401
+ pages := await_all(jobs)
402
+
403
+ job := &amp;:: =&gt; expensive_io()
404
+ = job.await(2.0)
405
+
406
+ hits := atom(0)
407
+ hits.swap(::n =&gt; n + 1)
408
+
409
+ queue := chan(8)
410
+ queue.send("hello")
411
+ = queue.recv(1.0)</code></pre></div>
412
+ <p>
413
+ Task methods are <code>await</code>, <code>done</code>, <code>status</code>, and <code>cancel</code>.
414
+ <code>race(tasks)</code> cancels losers. <code>await_all(tasks)</code> preserves order and cancels unfinished siblings when one fails.
415
+ </p>
416
+ <p>
417
+ <code>parallel(values, fn, workers)</code> uses Ruby threads and suits files, networks, and subprocess waits.
418
+ <code>pmap(values, fn, workers)</code> uses Unix fork workers for CPU work and preserves input order.
419
+ </p>
420
+ </section>
421
+
422
+ <section id="commands" data-doc-section data-title="Structured command values">
423
+ <h2>14. Structured command values</h2>
424
+ <div class="listing"><pre><code>job := cmd("git", "rev-parse", "--verify", ref)
425
+ result := job.check()
426
+ sha := result.out.trim()</code></pre></div>
427
+ <table class="reference-table compact">
428
+ <tbody>
429
+ <tr><th><code>.argv()</code></th><td>Return a copy of the argument vector.</td></tr>
430
+ <tr><th><code>.run()</code></th><td>Inherit the terminal and return status.</td></tr>
431
+ <tr><th><code>.result()</code></th><td>Capture standard output, standard error, and status.</td></tr>
432
+ <tr><th><code>.capture()</code></th><td>Return standard output as a string.</td></tr>
433
+ <tr><th><code>.check()</code></th><td>Return the result or raise on nonzero status.</td></tr>
434
+ <tr><th><code>.task()</code></th><td>Run the command in an RSH task.</td></tr>
435
+ </tbody>
436
+ </table>
437
+ <p>Use <code>cmd()</code> when filenames, URLs, or other data must stay separate arguments and must not be parsed as shell text.</p>
438
+ </section>
439
+
440
+ <section id="bridge" data-doc-section data-title="C ABI bridge and cbuf">
441
+ <h2>15. C ABI bridges</h2>
442
+ <div class="warning"><strong>Warning:</strong> A wrong native signature can crash the SRSH process. Read the security notes before using pointers.</div>
443
+ <div class="listing"><pre><code>bridge libc from "libc.so.6"
444
+ getpid() -&gt; i32
445
+ strlen(cstr) -&gt; usize
446
+ gethostname(ptr, usize) -&gt; i32
447
+ end
448
+
449
+ buf := cbuf(256)
450
+ libc.gethostname(buf, buf.size())
451
+ = buf.string()</code></pre></div>
452
+ <p><code>@self</code> loads symbols from the current process. Supported ABI names are:</p>
453
+ <div class="word-list"><code>void bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 cstr ptr</code></div>
454
+ <p>Buffer methods include <code>size</code>, <code>address</code>, <code>ptr</code>, <code>read</code>, <code>write</code>, <code>string</code>, and <code>clear</code>.</p>
455
+ </section>
456
+
457
+ <section id="meta" data-doc-section data-title="Code values evaluation and reflection">
458
+ <h2>16. Code values and reflection</h2>
459
+ <div class="listing"><pre><code>code cleanup
460
+ rm build/tmp/cache
461
+ end
462
+
463
+ = sourceof(cleanup)
464
+ run(cleanup)
465
+
466
+ formula := "x * 3"
467
+ = eval(formula)</code></pre></div>
468
+ <p>
469
+ <code>code ... end</code> parses source without running it. Dynamic helpers are <code>eval</code>, <code>code</code>, <code>run</code>,
470
+ <code>valid</code>, and <code>sourceof</code>. Introspection functions include <code>locals</code>, <code>fns</code>, <code>protos</code>, and <code>traits</code>.
471
+ </p>
472
+ </section>
473
+
474
+ <section id="builtins" data-doc-section data-title="Built-in function reference">
475
+ <h2>17. Built-in function reference</h2>
476
+ <table class="reference-table compact">
477
+ <tbody>
478
+ <tr><th>Conversion</th><td><code>int float str bool type</code></td></tr>
479
+ <tr><th>Numbers and choice</th><td><code>round floor ceil sqrt clamp abs min max rand pick</code></td></tr>
480
+ <tr><th>Strings</th><td><code>len empty contains starts ends starts_with ends_with split join upper lower trim replace lines words</code></td></tr>
481
+ <tr><th>Maps</th><td><code>keys values fields</code></td></tr>
482
+ <tr><th>Environment</th><td><code>env cwd clock status cpu_count</code></td></tr>
483
+ <tr><th>Processes</th><td><code>capture sh cmd shellquote</code></td></tr>
484
+ <tr><th>Concurrency</th><td><code>spawn await await_all race parallel pmap chan atom sleep</code></td></tr>
485
+ <tr><th>Errors</th><td><code>attempt fail assert</code></td></tr>
486
+ <tr><th>Objects</th><td><code>clone fields methods protoof is</code></td></tr>
487
+ <tr><th>Files</th><td><code>readfile writefile appendfile exists file dir glob stat mkdirp rmfile cpfile mvfile basename dirname ext</code></td></tr>
488
+ <tr><th>Data</th><td><code>json json_dump</code></td></tr>
489
+ <tr><th>Code</th><td><code>eval code run sourceof valid locals fns protos traits</code></td></tr>
490
+ <tr><th>Native</th><td><code>cbuf</code></td></tr>
491
+ </tbody>
492
+ </table>
493
+ </section>
494
+
495
+ <section id="configuration" data-doc-section data-title="Shell options themes plugins and files">
496
+ <h2>18. Shell configuration</h2>
497
+ <p>Shell strictness is controlled with regular shell commands:</p>
498
+ <div class="listing"><pre><code>option pipefail yes
499
+ option nounset yes
500
+ option noclobber yes
501
+ option strict yes</code></pre></div>
502
+ <p><code>strict</code> enables <code>pipefail</code> and <code>nounset</code>. It does not copy Bash <code>set -e</code>.</p>
503
+ <table class="reference-table compact">
504
+ <tbody>
505
+ <tr><th><code>~/.srshrc</code></th><td>Startup commands</td></tr>
506
+ <tr><th><code>~/.srsh_history</code></th><td>Interactive history</td></tr>
507
+ <tr><th><code>~/.srsh/themes</code></th><td>Private <code>.theme</code> and <code>.json</code> theme files</td></tr>
508
+ <tr><th><code>~/.srsh/plugins</code></th><td>Private <code>.rsh</code> and trusted <code>.rb</code> plugins</td></tr>
509
+ </tbody>
510
+ </table>
511
+ <p>
512
+ Use <code>scheme --list</code>, <code>scheme NAME</code>, <code>plugins</code>, and <code>reload</code> to manage the interactive environment.
513
+ Ruby plugins are trusted code. Plugin and theme files must pass owner and mode checks before automatic loading.
514
+ </p>
515
+ <p>Core shell commands are:</p>
516
+ <div class="word-list"><code>cd pwd put echo printf ls alias unalias set export unset read true false sleep source . exit quit help hist clearhist scheme theme themes plugins reload jobs wait fg bg exec systemfetch which type dirs pushd popd umask kill option</code></div>
517
+ <p>A simple theme file uses ANSI SGR values:</p>
518
+ <div class="listing"><pre><code># ~/.srsh/themes/amber.theme
519
+ border=1;33
520
+ title=1;37
521
+ key=33
522
+ value=0;37
523
+ ok=32
524
+ warn=33
525
+ error=31
526
+ dim=90
527
+ path=33
528
+ host=36
529
+ mark=35</code></pre></div>
530
+ <p>Ruby plugins receive the <code>SRSH</code> API and may register builtins, hooks, aliases, and themes. RSH plugin files run as ordinary RSH scripts.</p>
531
+ </section>
532
+
533
+ <p class="back-top"><a href="#/manual">Back to the top of the manual</a></p>
534
+ </article>
535
+ </div>
536
+ </section>
537
+
538
+ <section class="page" data-page="download">
539
+ <div class="page-heading">
540
+ <h1>Download SRSH</h1>
541
+ <p>Release artifacts are produced from a matching version tag after the test suite and release checks pass.</p>
542
+ </div>
543
+
544
+ <div class="download-panel">
545
+ <h2>Latest release: <span data-latest-version>1.0.0</span></h2>
546
+ <p data-release-status>The site will read the latest published release when GitHub is available.</p>
547
+ <p class="download-buttons">
548
+ <a class="download-link" data-download="gem" href="https://github.com/RobertFlexx/RSH/releases/latest/download/srsh.gem">Download gem</a>
549
+ <a class="download-link secondary" data-download="source" href="https://github.com/RobertFlexx/RSH/releases/latest/download/srsh-source.tar.gz">Download source</a>
550
+ <a class="download-link secondary" data-download="checksums" href="https://github.com/RobertFlexx/RSH/releases/latest/download/SHA256SUMS">SHA256SUMS</a>
551
+ </p>
552
+ <p><a href="https://github.com/RobertFlexx/RSH/releases/latest">Open the latest release on GitHub</a></p>
553
+ </div>
554
+
555
+ <h2>Install the gem file</h2>
556
+ <p>Ruby 3.2 or newer is required. Building the optional native extension also needs a C compiler and Ruby development headers.</p>
557
+ <div class="listing">
558
+ <pre id="install-gem"><code>gem install ./srsh.gem
559
+ srsh --version
560
+ srsh</code></pre>
561
+ <button class="copy-button" type="button" data-copy-target="install-gem">copy</button>
562
+ </div>
563
+
564
+ <h2>Run from the source archive</h2>
565
+ <p>The shell can run as Ruby-only code. The native helper is optional when running from a checkout or source archive.</p>
566
+ <div class="listing">
567
+ <pre id="install-source"><code>tar -xzf srsh-source.tar.gz
568
+ cd srsh-1.0.0
569
+ make test
570
+ ./bin/srsh</code></pre>
571
+ <button class="copy-button" type="button" data-copy-target="install-source">copy</button>
572
+ </div>
573
+
574
+ <h2>Install from a checkout</h2>
575
+ <div class="listing">
576
+ <pre id="install-checkout"><code>git clone https://github.com/RobertFlexx/RSH.git
577
+ cd RSH
578
+ make test
579
+ make PREFIX="$HOME/.local" install</code></pre>
580
+ <button class="copy-button" type="button" data-copy-target="install-checkout">copy</button>
581
+ </div>
582
+ <p>The direct branch snapshot is also available as a <a href="https://github.com/RobertFlexx/RSH/archive/refs/heads/master.zip">zip file</a>.</p>
583
+
584
+ <h2>Verify a download</h2>
585
+ <div class="listing"><pre><code>sha256sum -c SHA256SUMS</code></pre></div>
586
+ <p>The checksum file covers the stable and versioned artifacts attached to the release.</p>
587
+ </section>
588
+
589
+ <section class="page" data-page="examples">
590
+ <div class="page-heading">
591
+ <h1>RSH examples</h1>
592
+ <p>These examples correspond to programs in the repository and use features present in the 1.0 tree.</p>
593
+ </div>
594
+
595
+ <h2>Filter files with a value pipeline</h2>
596
+ <div class="listing"><pre><code>root := $1
597
+ ? root == "" =&gt; root := "."
598
+
599
+ ruby := glob(root ++ "/**/*.rb")
600
+ |&gt; reject(::path =&gt; contains(path, "/vendor/"))
601
+ |&gt; map(::path =&gt; %[path: path, bytes: len(readfile(path))])
602
+ |&gt; sort(::item =&gt; 0 - item.bytes)
603
+
604
+ @ ruby -&gt; item =&gt; = "#{item.bytes} #{item.path}"</code></pre></div>
605
+ <p><a href="https://github.com/RobertFlexx/RSH/blob/master/examples/hot.rsh">View examples/hot.rsh</a></p>
606
+
607
+ <h2>Use a module</h2>
608
+ <div class="listing"><pre><code>use "./modules/text.rsh" as text
609
+
610
+ rows := [" hello ", "", "world"] |&gt; text.clean
611
+ @ rows -&gt; row =&gt; = text.tag(row)</code></pre></div>
612
+ <p><a href="https://github.com/RobertFlexx/RSH/blob/master/examples/modules.rsh">View examples/modules.rsh</a></p>
613
+
614
+ <h2>Run tasks together</h2>
615
+ <div class="listing"><pre><code>space sys
616
+ task kernel() =&gt;
617
+ cmd("uname", "-srmo").check().out.trim()
618
+
619
+ task uptime() =&gt;
620
+ cmd("uptime", "-p").check().out.trim()
621
+ end
622
+
623
+ jobs := [sys.kernel(), sys.uptime()]
624
+ = await_all(jobs)</code></pre></div>
625
+ <p><a href="https://github.com/RobertFlexx/RSH/blob/master/examples/paste.rsh">View examples/paste.rsh</a></p>
626
+
627
+ <h2>Call the C ABI</h2>
628
+ <div class="listing"><pre><code>bridge c from "@self"
629
+ strlen(cstr) -&gt; usize
630
+ end
631
+
632
+ = c.strlen("simple ruby shell")</code></pre></div>
633
+ <p><a href="https://github.com/RobertFlexx/RSH/blob/master/examples/bridge.rsh">View examples/bridge.rsh</a></p>
634
+
635
+ <h2>More programs</h2>
636
+ <ul>
637
+ <li><a href="https://github.com/RobertFlexx/RSH/blob/master/examples/tour.rsh">Language tour</a></li>
638
+ <li><a href="https://github.com/RobertFlexx/RSH/blob/master/examples/power.rsh">Prototypes, tasks, channels, errors, and structured commands</a></li>
639
+ <li><a href="https://github.com/RobertFlexx/RSH/blob/master/examples/defer.rsh">Deferred cleanup</a></li>
640
+ <li><a href="https://github.com/RobertFlexx/RSH/blob/master/examples/meta.rsh">Parsed code and evaluation</a></li>
641
+ <li><a href="https://github.com/RobertFlexx/RSH/blob/master/examples/calculator.rsh">A larger calculator program</a></li>
642
+ </ul>
643
+ </section>
644
+
645
+ <section class="page" data-page="project">
646
+ <div class="page-heading">
647
+ <h1>Project information</h1>
648
+ <p>SRSH is developed in the open and distributed under the MIT License.</p>
649
+ </div>
650
+
651
+ <h2>Source and issues</h2>
652
+ <table class="reference-table project-links">
653
+ <tbody>
654
+ <tr><th>Repository</th><td><a href="https://github.com/RobertFlexx/RSH">github.com/RobertFlexx/RSH</a></td></tr>
655
+ <tr><th>Releases</th><td><a href="https://github.com/RobertFlexx/RSH/releases">GitHub release archive</a></td></tr>
656
+ <tr><th>Issues</th><td><a href="https://github.com/RobertFlexx/RSH/issues">Bug reports and feature requests</a></td></tr>
657
+ <tr><th>License</th><td><a href="https://github.com/RobertFlexx/RSH/blob/master/LICENSE">MIT License</a></td></tr>
658
+ </tbody>
659
+ </table>
660
+
661
+ <h2>Repository layout</h2>
662
+ <div class="listing"><pre><code>bin/srsh command entry point
663
+ lib/srsh/ shell and language implementation
664
+ lib/srsh/language/ RSH lexer, parser, values, evaluator
665
+ lib/srsh/shell/ command lexer, executor, jobs, terminal
666
+ ext/srsh_native/ optional C helper
667
+ examples/ sample RSH programs
668
+ language-docs/ long-form language and security notes
669
+ docs/ this static site
670
+ test/ language and shell tests</code></pre></div>
671
+
672
+ <h2>Release policy</h2>
673
+ <p>
674
+ A tag such as <code>v1.0.0</code> must match <code>Srsh::VERSION</code>. The release workflow runs syntax checks,
675
+ the test suite, example validation, smoke tests, and the native build. It then creates gem and source artifacts,
676
+ writes SHA-256 checksums, and creates the GitHub release at that tag.
677
+ </p>
678
+
679
+ <h2>Scope</h2>
680
+ <p>
681
+ SRSH is a shell and a language implementation under active development. It is not a secure sandbox and it is not
682
+ a drop-in Bash interpreter. Ruby plugins and C bridges are trusted, process-level extension points.
683
+ </p>
684
+ </section>
685
+ </main>
686
+
687
+ <footer class="site-footer">
688
+ <div class="site-width">
689
+ <span>SRSH 1.0 documentation</span>
690
+ <span>|</span>
691
+ <a href="#/manual">Manual</a>
692
+ <span>|</span>
693
+ <a href="#/download">Download</a>
694
+ <span>|</span>
695
+ <a href="https://github.com/RobertFlexx/RSH">Source</a>
696
+ <span>|</span>
697
+ <span>No tracking or cookies</span>
698
+ </div>
699
+ </footer>
700
+
701
+ <div class="copy-notice" data-copy-notice role="status" aria-live="polite" hidden>Copied.</div>
702
+ </body>
703
+ </html>