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.
- checksums.yaml +4 -4
- data/LICENSE +18 -14
- data/README.md +351 -134
- data/bin/srsh +53 -1473
- data/docs/assets/slut.txt +4 -0
- data/docs/assets/srsh-mark.svg +12 -0
- data/docs/css/style.css +696 -0
- data/docs/index.html +703 -0
- data/docs/js/app.js +203 -0
- data/examples/bridge.rsh +8 -0
- data/examples/calculator.rsh +253 -0
- data/examples/defer.rsh +14 -0
- data/examples/hot.rsh +14 -0
- data/examples/meta.rsh +20 -0
- data/examples/modules/text.rsh +6 -0
- data/examples/modules.rsh +6 -0
- data/examples/paste.rsh +15 -0
- data/examples/plugin.rb +8 -0
- data/examples/power.rsh +65 -0
- data/examples/tour.rsh +38 -0
- data/ext/srsh_native/extconf.rb +3 -0
- data/ext/srsh_native/srsh_native.c +48 -0
- data/language-docs/LANGUAGE.md +670 -0
- data/language-docs/MIGRATION.md +44 -0
- data/language-docs/SECURITY.md +44 -0
- data/lib/srsh/app.rb +261 -0
- data/lib/srsh/builtins.rb +492 -0
- data/lib/srsh/editor.rb +530 -0
- data/lib/srsh/errors.rb +23 -0
- data/lib/srsh/history.rb +74 -0
- data/lib/srsh/language/evaluator.rb +1175 -0
- data/lib/srsh/language/lexer.rb +316 -0
- data/lib/srsh/language/parser.rb +997 -0
- data/lib/srsh/language/token.rb +5 -0
- data/lib/srsh/language/values.rb +392 -0
- data/lib/srsh/paths.rb +29 -0
- data/lib/srsh/plugins.rb +59 -0
- data/lib/srsh/process_identity.rb +38 -0
- data/lib/srsh/security.rb +38 -0
- data/lib/srsh/shell/executor.rb +1182 -0
- data/lib/srsh/shell/job.rb +101 -0
- data/lib/srsh/shell/lexer.rb +114 -0
- data/lib/srsh/shell/terminal.rb +26 -0
- data/lib/srsh/state.rb +136 -0
- data/lib/srsh/theme.rb +108 -0
- data/lib/srsh/version.rb +1 -2
- data/lib/srsh.rb +15 -0
- 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 | Requires Ruby 3.2 or newer | 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
|
+
|> reject(::path => contains(path, "/vendor/"))
|
|
56
|
+
|> sort
|
|
57
|
+
|
|
58
|
+
? files |> len > 20 => = "#{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 |> 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] |> 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 && put built
|
|
185
|
+
generate > output.txt 2> errors.txt
|
|
186
|
+
long_job &
|
|
187
|
+
jobs
|
|
188
|
+
fg %1</code></pre></div>
|
|
189
|
+
<p>
|
|
190
|
+
Implemented operators are <code>|</code>, <code>&&</code>, <code>||</code>, <code>;</code>, and background <code>&</code>.
|
|
191
|
+
Redirections include <code><</code>, <code>></code>, <code>>></code>, <code>2></code>, and <code>2>></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 ..< 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 &&
|
|
246
|
+
== != === !== =~ !~ in
|
|
247
|
+
< <= > >=
|
|
248
|
+
|>
|
|
249
|
+
.. ..<
|
|
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
|
+
|> map(::path => basename(path))
|
|
257
|
+
|> uniq
|
|
258
|
+
|> 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) => x * x
|
|
268
|
+
|
|
269
|
+
double := ::x => x * 2
|
|
270
|
+
sum_rest := ::(head, *tail) => tail |> 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 >= 90
|
|
285
|
+
emit "great"
|
|
286
|
+
else
|
|
287
|
+
emit "keep going"
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
each users -> 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 -> 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 => emit "go"
|
|
301
|
+
@ users -> user => = 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>^></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 => = "ok"
|
|
313
|
+
| [401, 403] => = "auth"
|
|
314
|
+
| ? it >= 500 => = "server"
|
|
315
|
+
| _ => = "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 => x % 2 == 0)
|
|
329
|
+
.map(::x => x ** 2)
|
|
330
|
+
.sum()
|
|
331
|
+
|
|
332
|
+
by_ext := files |> group(::path => 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() => "#{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) => 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(:: => 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 |> map(fetch)
|
|
401
|
+
pages := await_all(jobs)
|
|
402
|
+
|
|
403
|
+
job := &:: => expensive_io()
|
|
404
|
+
= job.await(2.0)
|
|
405
|
+
|
|
406
|
+
hits := atom(0)
|
|
407
|
+
hits.swap(::n => 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() -> i32
|
|
445
|
+
strlen(cstr) -> usize
|
|
446
|
+
gethostname(ptr, usize) -> 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 == "" => root := "."
|
|
598
|
+
|
|
599
|
+
ruby := glob(root ++ "/**/*.rb")
|
|
600
|
+
|> reject(::path => contains(path, "/vendor/"))
|
|
601
|
+
|> map(::path => %[path: path, bytes: len(readfile(path))])
|
|
602
|
+
|> sort(::item => 0 - item.bytes)
|
|
603
|
+
|
|
604
|
+
@ ruby -> item => = "#{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"] |> text.clean
|
|
611
|
+
@ rows -> row => = 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() =>
|
|
617
|
+
cmd("uname", "-srmo").check().out.trim()
|
|
618
|
+
|
|
619
|
+
task uptime() =>
|
|
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) -> 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>
|