srsh 0.8.0 → 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 +12 -3
- data/README.md +446 -8
- data/bin/srsh +71 -0
- 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 +3 -0
- data/lib/srsh.rb +11 -5
- metadata +61 -14
- data/exe/srsh +0 -6
- data/lib/srsh/runner.rb +0 -2416
data/docs/js/app.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const repository = "https://github.com/RobertFlexx/RSH";
|
|
5
|
+
const api = "https://api.github.com/repos/RobertFlexx/RSH/releases/latest";
|
|
6
|
+
const pages = new Map();
|
|
7
|
+
|
|
8
|
+
document.querySelectorAll("[data-page]").forEach(function (page) {
|
|
9
|
+
pages.set(page.getAttribute("data-page"), page);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
function routeFromHash() {
|
|
13
|
+
const raw = (window.location.hash || "#/").slice(1);
|
|
14
|
+
const pieces = raw.split("#");
|
|
15
|
+
const path = (pieces[0] || "/").replace(/^\//, "");
|
|
16
|
+
return {
|
|
17
|
+
page: path || "home",
|
|
18
|
+
anchor: pieces[1] || ""
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function showRoute() {
|
|
23
|
+
const route = routeFromHash();
|
|
24
|
+
const pageName = pages.has(route.page) ? route.page : "home";
|
|
25
|
+
|
|
26
|
+
pages.forEach(function (page, name) {
|
|
27
|
+
page.classList.toggle("active", name === pageName);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
document.querySelectorAll("[data-route]").forEach(function (link) {
|
|
31
|
+
const current = link.getAttribute("data-route") === pageName;
|
|
32
|
+
link.classList.toggle("current", current);
|
|
33
|
+
if (current) link.setAttribute("aria-current", "page");
|
|
34
|
+
else link.removeAttribute("aria-current");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const titles = {
|
|
38
|
+
home: "SRSH 1.0: Simple Ruby Shell",
|
|
39
|
+
manual: "RSH language manual | SRSH 1.0",
|
|
40
|
+
download: "Download SRSH 1.0",
|
|
41
|
+
examples: "RSH examples | SRSH 1.0",
|
|
42
|
+
project: "Project information | SRSH 1.0"
|
|
43
|
+
};
|
|
44
|
+
document.title = titles[pageName] || titles.home;
|
|
45
|
+
|
|
46
|
+
document.querySelectorAll(".toc a").forEach(function (link) {
|
|
47
|
+
link.classList.toggle("current", Boolean(route.anchor) && link.getAttribute("href").endsWith("#" + route.anchor));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (route.anchor) {
|
|
51
|
+
window.setTimeout(function () {
|
|
52
|
+
const target = document.getElementById(route.anchor);
|
|
53
|
+
if (target) target.scrollIntoView({ block: "start" });
|
|
54
|
+
}, 0);
|
|
55
|
+
} else {
|
|
56
|
+
window.scrollTo(0, 0);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
window.addEventListener("hashchange", showRoute);
|
|
61
|
+
showRoute();
|
|
62
|
+
|
|
63
|
+
const notice = document.querySelector("[data-copy-notice]");
|
|
64
|
+
let noticeTimer;
|
|
65
|
+
|
|
66
|
+
function showNotice(message) {
|
|
67
|
+
if (!notice) return;
|
|
68
|
+
notice.textContent = message;
|
|
69
|
+
notice.hidden = false;
|
|
70
|
+
window.clearTimeout(noticeTimer);
|
|
71
|
+
noticeTimer = window.setTimeout(function () {
|
|
72
|
+
notice.hidden = true;
|
|
73
|
+
}, 1400);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function fallbackCopy(text) {
|
|
77
|
+
const field = document.createElement("textarea");
|
|
78
|
+
field.value = text;
|
|
79
|
+
field.setAttribute("readonly", "");
|
|
80
|
+
field.style.position = "fixed";
|
|
81
|
+
field.style.left = "-10000px";
|
|
82
|
+
document.body.appendChild(field);
|
|
83
|
+
field.select();
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
document.execCommand("copy");
|
|
87
|
+
showNotice("Copied.");
|
|
88
|
+
} catch (error) {
|
|
89
|
+
showNotice("Copy failed.");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
field.remove();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
document.addEventListener("click", function (event) {
|
|
96
|
+
const button = event.target.closest("[data-copy-target]");
|
|
97
|
+
if (!button) return;
|
|
98
|
+
|
|
99
|
+
const target = document.getElementById(button.getAttribute("data-copy-target"));
|
|
100
|
+
if (!target) return;
|
|
101
|
+
const text = target.textContent.replace(/\n$/, "");
|
|
102
|
+
|
|
103
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
104
|
+
navigator.clipboard.writeText(text).then(function () {
|
|
105
|
+
showNotice("Copied.");
|
|
106
|
+
}).catch(function () {
|
|
107
|
+
fallbackCopy(text);
|
|
108
|
+
});
|
|
109
|
+
} else {
|
|
110
|
+
fallbackCopy(text);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const searchForm = document.querySelector("[data-doc-search]");
|
|
115
|
+
const searchResults = document.querySelector("[data-search-results]");
|
|
116
|
+
|
|
117
|
+
if (searchForm && searchResults) {
|
|
118
|
+
searchForm.addEventListener("submit", function (event) {
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
const input = searchForm.querySelector("input[type='search']");
|
|
121
|
+
const query = input ? input.value.trim().toLowerCase() : "";
|
|
122
|
+
searchResults.replaceChildren();
|
|
123
|
+
|
|
124
|
+
if (!query) {
|
|
125
|
+
window.location.hash = "#/manual";
|
|
126
|
+
searchResults.hidden = true;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const matches = Array.from(document.querySelectorAll("[data-doc-section]")).filter(function (section) {
|
|
131
|
+
return section.textContent.toLowerCase().includes(query);
|
|
132
|
+
}).slice(0, 10);
|
|
133
|
+
|
|
134
|
+
const heading = document.createElement("strong");
|
|
135
|
+
heading.textContent = matches.length ? "Manual entries:" : "No manual entries found.";
|
|
136
|
+
searchResults.appendChild(heading);
|
|
137
|
+
|
|
138
|
+
if (matches.length) {
|
|
139
|
+
const list = document.createElement("ul");
|
|
140
|
+
matches.forEach(function (section) {
|
|
141
|
+
const item = document.createElement("li");
|
|
142
|
+
const link = document.createElement("a");
|
|
143
|
+
link.href = "#/manual#" + section.id;
|
|
144
|
+
link.textContent = section.getAttribute("data-title") || section.id;
|
|
145
|
+
item.appendChild(link);
|
|
146
|
+
list.appendChild(item);
|
|
147
|
+
});
|
|
148
|
+
searchResults.appendChild(list);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
searchResults.hidden = false;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function findAsset(release, preferredName, pattern) {
|
|
156
|
+
const assets = Array.isArray(release.assets) ? release.assets : [];
|
|
157
|
+
return assets.find(function (asset) {
|
|
158
|
+
return asset.name === preferredName;
|
|
159
|
+
}) || assets.find(function (asset) {
|
|
160
|
+
return pattern.test(asset.name);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function setDownload(kind, url) {
|
|
165
|
+
if (!url) return;
|
|
166
|
+
document.querySelectorAll("[data-download='" + kind + "']").forEach(function (link) {
|
|
167
|
+
link.href = url;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
fetch(api, {
|
|
172
|
+
headers: { "Accept": "application/vnd.github+json" }
|
|
173
|
+
}).then(function (response) {
|
|
174
|
+
if (!response.ok) throw new Error("No published release");
|
|
175
|
+
return response.json();
|
|
176
|
+
}).then(function (release) {
|
|
177
|
+
const tag = String(release.tag_name || "1.0.0");
|
|
178
|
+
const version = tag.replace(/^v/, "");
|
|
179
|
+
const gem = findAsset(release, "srsh.gem", /^srsh-[0-9].*\.gem$/);
|
|
180
|
+
const source = findAsset(release, "srsh-source.tar.gz", /^srsh-[0-9].*\.tar\.gz$/);
|
|
181
|
+
const checksums = findAsset(release, "SHA256SUMS", /^SHA256SUMS$/);
|
|
182
|
+
|
|
183
|
+
document.querySelectorAll("[data-latest-version]").forEach(function (element) {
|
|
184
|
+
element.textContent = version;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
setDownload("gem", gem && gem.browser_download_url);
|
|
188
|
+
setDownload("source", source && source.browser_download_url);
|
|
189
|
+
setDownload("checksums", checksums && checksums.browser_download_url);
|
|
190
|
+
|
|
191
|
+
const date = release.published_at ? new Date(release.published_at).toLocaleDateString() : "";
|
|
192
|
+
document.querySelectorAll("[data-release-status]").forEach(function (element) {
|
|
193
|
+
element.textContent = date ? "Published " + date + "." : "Published on GitHub.";
|
|
194
|
+
});
|
|
195
|
+
}).catch(function () {
|
|
196
|
+
document.querySelectorAll("[data-release-status]").forEach(function (element) {
|
|
197
|
+
element.textContent = "Release details are available from the GitHub release page.";
|
|
198
|
+
});
|
|
199
|
+
setDownload("gem", repository + "/releases/latest/download/srsh.gem");
|
|
200
|
+
setDownload("source", repository + "/releases/latest/download/srsh-source.tar.gz");
|
|
201
|
+
setDownload("checksums", repository + "/releases/latest/download/SHA256SUMS");
|
|
202
|
+
});
|
|
203
|
+
})();
|
data/examples/bridge.rsh
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
#!/usr/bin/env srsh
|
|
2
|
+
|
|
3
|
+
emit "SRSH Calculator"
|
|
4
|
+
emit "type help for commands"
|
|
5
|
+
emit ""
|
|
6
|
+
|
|
7
|
+
$CALC_SRC := ""
|
|
8
|
+
$CALC_POS := 0
|
|
9
|
+
$CALC_ERR := ""
|
|
10
|
+
$CALC_ANS := 0
|
|
11
|
+
$CALC_RUN := "yes"
|
|
12
|
+
|
|
13
|
+
:: peek()
|
|
14
|
+
? int($CALC_POS) >= len($CALC_SRC)
|
|
15
|
+
^ ""
|
|
16
|
+
.?
|
|
17
|
+
^ $CALC_SRC[int($CALC_POS)]
|
|
18
|
+
.::
|
|
19
|
+
|
|
20
|
+
:: bump()
|
|
21
|
+
$CALC_POS := int($CALC_POS) + 1
|
|
22
|
+
^ 0
|
|
23
|
+
.::
|
|
24
|
+
|
|
25
|
+
:: skip()
|
|
26
|
+
@? int($CALC_POS) < len($CALC_SRC)
|
|
27
|
+
c := peek()
|
|
28
|
+
? c == " " or c == "\t"
|
|
29
|
+
ignored := bump()
|
|
30
|
+
:?
|
|
31
|
+
^!
|
|
32
|
+
.?
|
|
33
|
+
.@
|
|
34
|
+
^ 0
|
|
35
|
+
.::
|
|
36
|
+
|
|
37
|
+
:: number()
|
|
38
|
+
ignored := skip()
|
|
39
|
+
text := ""
|
|
40
|
+
dots := 0
|
|
41
|
+
digits := 0
|
|
42
|
+
|
|
43
|
+
@? int($CALC_POS) < len($CALC_SRC)
|
|
44
|
+
c := peek()
|
|
45
|
+
? contains("0123456789", c)
|
|
46
|
+
text ++= c
|
|
47
|
+
digits += 1
|
|
48
|
+
ignored := bump()
|
|
49
|
+
:?
|
|
50
|
+
? c == "." and dots == 0
|
|
51
|
+
text ++= c
|
|
52
|
+
dots := 1
|
|
53
|
+
ignored := bump()
|
|
54
|
+
:?
|
|
55
|
+
^!
|
|
56
|
+
.?
|
|
57
|
+
.?
|
|
58
|
+
.@
|
|
59
|
+
|
|
60
|
+
? digits == 0
|
|
61
|
+
$CALC_ERR := "expected number at column " ++ str(int($CALC_POS) + 1)
|
|
62
|
+
^ 0
|
|
63
|
+
.?
|
|
64
|
+
|
|
65
|
+
^ float(text)
|
|
66
|
+
.::
|
|
67
|
+
|
|
68
|
+
:: ipow(base, exponent)
|
|
69
|
+
n := int(exponent)
|
|
70
|
+
? float(n) != float(exponent)
|
|
71
|
+
$CALC_ERR := "^ requires an integer exponent"
|
|
72
|
+
^ 0
|
|
73
|
+
.?
|
|
74
|
+
|
|
75
|
+
negative := no
|
|
76
|
+
? n < 0
|
|
77
|
+
negative := yes
|
|
78
|
+
n := 0 - n
|
|
79
|
+
.?
|
|
80
|
+
|
|
81
|
+
result := 1.0
|
|
82
|
+
@ n -> i
|
|
83
|
+
result *= base
|
|
84
|
+
.@
|
|
85
|
+
|
|
86
|
+
? negative
|
|
87
|
+
? result == 0
|
|
88
|
+
$CALC_ERR := "division by zero"
|
|
89
|
+
^ 0
|
|
90
|
+
.?
|
|
91
|
+
result := 1.0 / result
|
|
92
|
+
.?
|
|
93
|
+
^ result
|
|
94
|
+
.::
|
|
95
|
+
|
|
96
|
+
:: primary()
|
|
97
|
+
ignored := skip()
|
|
98
|
+
c := peek()
|
|
99
|
+
|
|
100
|
+
? c == ""
|
|
101
|
+
$CALC_ERR := "expected value"
|
|
102
|
+
^ 0
|
|
103
|
+
.?
|
|
104
|
+
|
|
105
|
+
? c == "+"
|
|
106
|
+
ignored := bump()
|
|
107
|
+
^ primary()
|
|
108
|
+
.?
|
|
109
|
+
|
|
110
|
+
? c == "-"
|
|
111
|
+
ignored := bump()
|
|
112
|
+
^ 0 - primary()
|
|
113
|
+
.?
|
|
114
|
+
|
|
115
|
+
? c == "("
|
|
116
|
+
ignored := bump()
|
|
117
|
+
value := expression()
|
|
118
|
+
ignored := skip()
|
|
119
|
+
? peek() != ")"
|
|
120
|
+
? $CALC_ERR == ""
|
|
121
|
+
$CALC_ERR := "missing ')' at column " ++ str(int($CALC_POS) + 1)
|
|
122
|
+
.?
|
|
123
|
+
^ value
|
|
124
|
+
.?
|
|
125
|
+
ignored := bump()
|
|
126
|
+
^ value
|
|
127
|
+
.?
|
|
128
|
+
|
|
129
|
+
^ number()
|
|
130
|
+
.::
|
|
131
|
+
|
|
132
|
+
:: power()
|
|
133
|
+
left := primary()
|
|
134
|
+
? $CALC_ERR != ""
|
|
135
|
+
^ left
|
|
136
|
+
.?
|
|
137
|
+
|
|
138
|
+
ignored := skip()
|
|
139
|
+
? peek() == "^"
|
|
140
|
+
ignored := bump()
|
|
141
|
+
right := power()
|
|
142
|
+
^ ipow(left, right)
|
|
143
|
+
.?
|
|
144
|
+
^ left
|
|
145
|
+
.::
|
|
146
|
+
|
|
147
|
+
:: term()
|
|
148
|
+
value := power()
|
|
149
|
+
|
|
150
|
+
@? $CALC_ERR == ""
|
|
151
|
+
ignored := skip()
|
|
152
|
+
op := peek()
|
|
153
|
+
? op != "*" and op != "/" and op != "%"
|
|
154
|
+
^!
|
|
155
|
+
.?
|
|
156
|
+
|
|
157
|
+
ignored := bump()
|
|
158
|
+
rhs := power()
|
|
159
|
+
? $CALC_ERR != ""
|
|
160
|
+
^ value
|
|
161
|
+
.?
|
|
162
|
+
|
|
163
|
+
? op == "*"
|
|
164
|
+
value *= rhs
|
|
165
|
+
:?
|
|
166
|
+
? op == "/"
|
|
167
|
+
? rhs == 0
|
|
168
|
+
$CALC_ERR := "division by zero"
|
|
169
|
+
^ value
|
|
170
|
+
.?
|
|
171
|
+
value /= rhs
|
|
172
|
+
:?
|
|
173
|
+
? rhs == 0
|
|
174
|
+
$CALC_ERR := "modulo by zero"
|
|
175
|
+
^ value
|
|
176
|
+
.?
|
|
177
|
+
value := int(value) % int(rhs)
|
|
178
|
+
.?
|
|
179
|
+
.?
|
|
180
|
+
.@
|
|
181
|
+
^ value
|
|
182
|
+
.::
|
|
183
|
+
|
|
184
|
+
:: expression()
|
|
185
|
+
value := term()
|
|
186
|
+
|
|
187
|
+
@? $CALC_ERR == ""
|
|
188
|
+
ignored := skip()
|
|
189
|
+
op := peek()
|
|
190
|
+
? op != "+" and op != "-"
|
|
191
|
+
^!
|
|
192
|
+
.?
|
|
193
|
+
|
|
194
|
+
ignored := bump()
|
|
195
|
+
rhs := term()
|
|
196
|
+
? $CALC_ERR != ""
|
|
197
|
+
^ value
|
|
198
|
+
.?
|
|
199
|
+
|
|
200
|
+
? op == "+"
|
|
201
|
+
value += rhs
|
|
202
|
+
:?
|
|
203
|
+
value -= rhs
|
|
204
|
+
.?
|
|
205
|
+
.@
|
|
206
|
+
^ value
|
|
207
|
+
.::
|
|
208
|
+
|
|
209
|
+
:: evaluate()
|
|
210
|
+
$CALC_POS := 0
|
|
211
|
+
$CALC_ERR := ""
|
|
212
|
+
value := expression()
|
|
213
|
+
ignored := skip()
|
|
214
|
+
|
|
215
|
+
? $CALC_ERR == "" and int($CALC_POS) < len($CALC_SRC)
|
|
216
|
+
$CALC_ERR := "unexpected '" ++ peek() ++ "' at column " ++ str(int($CALC_POS) + 1)
|
|
217
|
+
.?
|
|
218
|
+
|
|
219
|
+
? $CALC_ERR != ""
|
|
220
|
+
emit "error: " ++ $CALC_ERR
|
|
221
|
+
^ 0
|
|
222
|
+
.?
|
|
223
|
+
|
|
224
|
+
$CALC_ANS := value
|
|
225
|
+
emit "= " ++ str(value)
|
|
226
|
+
^ value
|
|
227
|
+
.::
|
|
228
|
+
|
|
229
|
+
@? $CALC_RUN == "yes"
|
|
230
|
+
printf "calc> "
|
|
231
|
+
read CALC_SRC
|
|
232
|
+
?? $CALC_SRC
|
|
233
|
+
| "" ->
|
|
234
|
+
ignored := 0
|
|
235
|
+
| "q" ->
|
|
236
|
+
$CALC_RUN := "no"
|
|
237
|
+
| "quit" ->
|
|
238
|
+
$CALC_RUN := "no"
|
|
239
|
+
| "exit" ->
|
|
240
|
+
$CALC_RUN := "no"
|
|
241
|
+
| "ans" ->
|
|
242
|
+
emit "= " ++ str(float($CALC_ANS))
|
|
243
|
+
| "clear" ->
|
|
244
|
+
clear
|
|
245
|
+
| "help" ->
|
|
246
|
+
emit "operators: + - * / % ^ and parentheses"
|
|
247
|
+
emit "commands: ans, clear, help, quit"
|
|
248
|
+
| _ ->
|
|
249
|
+
ignored := evaluate()
|
|
250
|
+
.??
|
|
251
|
+
.@
|
|
252
|
+
|
|
253
|
+
emit "bye :P"
|
data/examples/defer.rsh
ADDED
data/examples/hot.rsh
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env srsh
|
|
2
|
+
|
|
3
|
+
root := $1
|
|
4
|
+
? root == "" => root := "."
|
|
5
|
+
branch := $(git branch --show-current 2>/dev/null)
|
|
6
|
+
? branch == "" => branch := "no-branch"
|
|
7
|
+
|
|
8
|
+
ruby := glob(root ++ "/**/*.rb")
|
|
9
|
+
|> reject(::p => contains(p, "/vendor/"))
|
|
10
|
+
|> map(::p => %[path: p, bytes: len(readfile(p))])
|
|
11
|
+
|> sort(::x => 0 - x.bytes)
|
|
12
|
+
|
|
13
|
+
= "#{len(ruby)} ruby files on #{branch}"
|
|
14
|
+
@ ruby -> item => = "#{item.bytes} #{item.path}"
|
data/examples/meta.rsh
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env srsh
|
|
2
|
+
|
|
3
|
+
name := $1
|
|
4
|
+
? name == "" => name := "gang"
|
|
5
|
+
dry := $2 == "--dry"
|
|
6
|
+
|
|
7
|
+
code greeting
|
|
8
|
+
emit "wsg #{name}"
|
|
9
|
+
emit "cwd=#{cwd()}"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
if dry
|
|
13
|
+
emit "would run:"
|
|
14
|
+
= sourceof(greeting)
|
|
15
|
+
else
|
|
16
|
+
run(greeting)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
formula := "len(name) * 2"
|
|
20
|
+
= "meta expression result=#{eval(formula)}"
|
data/examples/paste.rsh
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# This file is intentionally shaped like something you'd paste at the prompt.
|
|
2
|
+
space sys
|
|
3
|
+
task kernel() =>
|
|
4
|
+
cmd("uname", "-srmo").check().out.trim()
|
|
5
|
+
|
|
6
|
+
task uptime() =>
|
|
7
|
+
cmd("uptime", "-p").check().out.trim()
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
jobs := [
|
|
11
|
+
sys.kernel(),
|
|
12
|
+
sys.uptime()
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
= await_all(jobs)
|
data/examples/plugin.rb
ADDED
data/examples/power.rsh
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env srsh
|
|
2
|
+
|
|
3
|
+
# Objects, functions, tasks and safe process values in one script.
|
|
4
|
+
|
|
5
|
+
space mathx
|
|
6
|
+
fn square(x) => x * x
|
|
7
|
+
fn positive(x) => x > 0
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
trait Labeled
|
|
11
|
+
fn label() => "#{self.name}:#{self.bias}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
proto Worker(name, bias := 0) with Labeled
|
|
15
|
+
slot name := name
|
|
16
|
+
slot bias := bias
|
|
17
|
+
|
|
18
|
+
fn score(x) => mathx.square(x) + self.bias
|
|
19
|
+
|
|
20
|
+
task score_later(x)
|
|
21
|
+
sleep(0.002)
|
|
22
|
+
return self.score(x)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
workers := [0,1,2,3] |> map(::i => Worker("w#{i}", i))
|
|
27
|
+
jobs := enumerate(workers) |> map(::pair => pair[1].score_later(pair[0] + 2))
|
|
28
|
+
= "async scores=#{await_all(jobs)}"
|
|
29
|
+
|
|
30
|
+
hits := atom(0)
|
|
31
|
+
parallel(0 ..< 64, ::i => hits.swap(::n => n + 1), 8)
|
|
32
|
+
= "thread-safe hits=#{hits.get()}"
|
|
33
|
+
|
|
34
|
+
ch := chan(1)
|
|
35
|
+
task send_one(out, value)
|
|
36
|
+
out.send(value)
|
|
37
|
+
return value
|
|
38
|
+
end
|
|
39
|
+
producer := send_one(ch, "hello from task")
|
|
40
|
+
= ch.recv(1)
|
|
41
|
+
producer.await(1)
|
|
42
|
+
ch.close()
|
|
43
|
+
|
|
44
|
+
head, second, *tail := [10,20,30,40]
|
|
45
|
+
= "destructure=#{head + second}; rest=#{tail}"
|
|
46
|
+
|
|
47
|
+
try
|
|
48
|
+
assert(workers |> len == 4, "worker count changed")
|
|
49
|
+
= workers |> map(::w => w.label())
|
|
50
|
+
catch err
|
|
51
|
+
= "error: #{err.message}"
|
|
52
|
+
finally
|
|
53
|
+
emit "checked workers"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
safe := cmd("printf", "%s", "argv stays data")
|
|
57
|
+
= safe.capture()
|
|
58
|
+
|
|
59
|
+
code later
|
|
60
|
+
= "code values are parsed before they run"
|
|
61
|
+
end
|
|
62
|
+
run(later)
|
|
63
|
+
|
|
64
|
+
fn cube(x) => x ** 3
|
|
65
|
+
= "functional=#{[1,2,3,4] |> filter(mathx.positive) |> map(cube) |> sum}"
|
data/examples/tour.rsh
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# RSH 1.0 language tour
|
|
2
|
+
name := "Ruby shell"
|
|
3
|
+
ports := [22, 80, 443]
|
|
4
|
+
server := %[name: "main", tls: yes]
|
|
5
|
+
|
|
6
|
+
emit "hello from " ++ name
|
|
7
|
+
|
|
8
|
+
? 443 in ports
|
|
9
|
+
emit "https enabled"
|
|
10
|
+
:?
|
|
11
|
+
emit "no https"
|
|
12
|
+
.?
|
|
13
|
+
|
|
14
|
+
@ 3 -> tick
|
|
15
|
+
emit "tick " ++ str(tick + 1)
|
|
16
|
+
.@
|
|
17
|
+
|
|
18
|
+
:: greet(who, count := 2)
|
|
19
|
+
@ int(count) -> i
|
|
20
|
+
emit "hey " ++ str(who) ++ " #" ++ str(i + 1)
|
|
21
|
+
.@
|
|
22
|
+
^ count
|
|
23
|
+
.::
|
|
24
|
+
|
|
25
|
+
greet("gang", 3)
|
|
26
|
+
|
|
27
|
+
platform := env("OSTYPE") ?? "unknown"
|
|
28
|
+
?? platform
|
|
29
|
+
| "darwin" ->
|
|
30
|
+
emit "mac"
|
|
31
|
+
| "linux" ->
|
|
32
|
+
emit "linux"
|
|
33
|
+
| _ ->
|
|
34
|
+
emit "some unix-ish thing"
|
|
35
|
+
.??
|
|
36
|
+
|
|
37
|
+
# Normal shell remains normal shell.
|
|
38
|
+
printf 'srsh\n' | tr a-z A-Z
|