1 | #!/usr/bin/env bash
|
2 | #
|
3 | # Compare alternative shell designs!
|
4 | #
|
5 | # Usage:
|
6 | # test/shell-vs-shell.sh <function name>
|
7 |
|
8 | set -o nounset
|
9 | set -o pipefail
|
10 | set -o errexit
|
11 |
|
12 | readonly BASE_DIR=_tmp/shell-vs-shell
|
13 | readonly REPO_ROOT=$(cd $(dirname $0)/.. ; pwd)
|
14 |
|
15 | readonly TAB=$'\t'
|
16 |
|
17 | html-head() {
|
18 | PYTHONPATH=. doctools/html_head.py "$@"
|
19 | }
|
20 |
|
21 | cmark() {
|
22 | # copied from build/doc.sh
|
23 | PYTHONPATH=. doctools/cmark.py --toc-tag h2 --toc-tag h3 --toc-pretty-href "$@"
|
24 | }
|
25 |
|
26 | highlight-code() {
|
27 | PYTHONPATH=. doctools/oils_doc.py highlight "$@"
|
28 | }
|
29 |
|
30 | desc() {
|
31 | echo "$@" > description.txt
|
32 | }
|
33 |
|
34 | src() {
|
35 | local lang=$1
|
36 |
|
37 | local prog=src/$lang
|
38 |
|
39 | # read from stdin
|
40 | cat > $prog
|
41 |
|
42 | case $lang in
|
43 | (oil)
|
44 | $REPO_ROOT/bin/oil $prog | tee output/$lang.txt
|
45 | ;;
|
46 | (shpp)
|
47 | ~/git/languages/shell-plus-plus/build/shell/shpp $prog | tee output/$lang.txt
|
48 | ;;
|
49 | (*)
|
50 | die "Invalid language $lang"
|
51 | ;;
|
52 | esac
|
53 | }
|
54 |
|
55 | CASE-hello() {
|
56 |
|
57 | desc "Print a string"
|
58 |
|
59 | # TODO:
|
60 | # - need to group these into a row somehow ...
|
61 | # code-begin or something
|
62 | # - setup script
|
63 | # - show output in the HTML too
|
64 | # - save that in a dir and then escape it
|
65 |
|
66 | src oil <<'EOF'
|
67 | # oil requires quotes
|
68 | echo 'hello world'
|
69 |
|
70 | const name = 'world'
|
71 | echo "hello $name"
|
72 | EOF
|
73 |
|
74 | src shpp <<'EOF'
|
75 | # no single quotes
|
76 | echo hello world
|
77 |
|
78 | name = "world"
|
79 | echo "hello ${name}" # braces required
|
80 | EOF
|
81 | }
|
82 |
|
83 | CASE-pipeline() {
|
84 | desc 'Pipeline and Glob'
|
85 |
|
86 | touch src{1,2,3,4,5}
|
87 |
|
88 | src oil <<EOF
|
89 | ls src* | sort -r | head -n 3
|
90 | EOF
|
91 |
|
92 | src shpp <<EOF
|
93 | ls src* | sort -r | head -n 3
|
94 | EOF
|
95 |
|
96 | }
|
97 |
|
98 | CASE-try() {
|
99 | desc 'Error Handling'
|
100 |
|
101 | src oil <<'EOF'
|
102 | # TODO: Oil could expose strerror() like shpp
|
103 |
|
104 | try zzz
|
105 | if (_status === 127) {
|
106 | echo "zzz not installed"
|
107 | }
|
108 | EOF
|
109 |
|
110 | src shpp <<'EOF'
|
111 | try {
|
112 | zzz
|
113 | } catch InvalidCmdException as ex {
|
114 | print("zzz not installed [msg: ", ex, "]")
|
115 | }
|
116 | EOF
|
117 | }
|
118 |
|
119 | CASE-read() {
|
120 | desc 'Read User Input'
|
121 |
|
122 | src oil <<'EOF'
|
123 | echo hello | read --line
|
124 |
|
125 | # _line starts with _, so it's a
|
126 | # "register" mutated by the interpreter
|
127 | echo "line: $_line"
|
128 | EOF
|
129 |
|
130 | src shpp <<'EOF'
|
131 | # not sure how to feed stdin
|
132 |
|
133 | line = read()
|
134 | print("line: ", line)
|
135 | EOF
|
136 | }
|
137 |
|
138 | # TODO: Sort by section
|
139 | CASE-ZZ-array() {
|
140 | desc 'Use Arrays'
|
141 |
|
142 | touch {foo,bar}.py
|
143 |
|
144 | src oil <<'EOF'
|
145 | const array1 = ['physics', 'chemistry', 1997, 2000]
|
146 | const array2 = [1, 2, 3, 4, 5, 6, 7]
|
147 |
|
148 | write -- "array1[0]: $[array1[0]]"
|
149 |
|
150 | # TODO: Oil needs @[]
|
151 | const slice = array2[1:5]
|
152 | write -- "array2[1:5]" @slice
|
153 |
|
154 | # use the word language: bare words, glob, brace expansion
|
155 | const shell_style = %( README.md *.py {alice,bob}@example.com )
|
156 | write -- @shell_style
|
157 | EOF
|
158 |
|
159 | src shpp <<'EOF'
|
160 | array1 = ["physics", "chemistry", 1997, 2000];
|
161 | array2 = [1, 2, 3, 4, 5, 6, 7 ];
|
162 |
|
163 | print("array1[0]: ", array1[0])
|
164 | print("array2[1:5]: ", array2[1:5])
|
165 | EOF
|
166 |
|
167 | }
|
168 |
|
169 | CASE-path() {
|
170 | desc 'Test if path exists'
|
171 |
|
172 | src oil <<'EOF'
|
173 | if test --exists /bin/grep {
|
174 | echo 'file exists'
|
175 | }
|
176 | EOF
|
177 |
|
178 | src shpp <<'EOF'
|
179 | if path("/bin/grep").exists() {
|
180 | echo "file exists"
|
181 | }
|
182 | EOF
|
183 | }
|
184 |
|
185 | test-one() {
|
186 | local func_name=$1
|
187 |
|
188 | echo "$TAB---"
|
189 | echo "$TAB$func_name"
|
190 | echo "$TAB---"
|
191 |
|
192 | local dir=$BASE_DIR/$func_name
|
193 |
|
194 | mkdir -p $dir/{src,output}
|
195 | pushd $dir >/dev/null
|
196 |
|
197 | $func_name
|
198 |
|
199 | popd >/dev/null
|
200 |
|
201 | }
|
202 |
|
203 | test-all() {
|
204 | rm -r -f -v $BASE_DIR/
|
205 | mkdir -p $BASE_DIR/
|
206 | compgen -A function | grep '^CASE-' | xargs -n 1 -- $0 test-one
|
207 |
|
208 | tree $BASE_DIR
|
209 | }
|
210 |
|
211 | html-escape() {
|
212 | sed 's/&/\&/g; s/</\</g; s/>/\>/g' "$@"
|
213 | }
|
214 |
|
215 | html-footer() {
|
216 | echo '
|
217 | </body>
|
218 | </html>
|
219 | '
|
220 | }
|
221 |
|
222 | # TODO: Run through doctools plugin for syntax highlighting
|
223 |
|
224 | make-table() {
|
225 | echo '<h2>'
|
226 | html-escape description.txt
|
227 | echo '</h2>'
|
228 |
|
229 | echo '<table>'
|
230 | echo ' <thead>'
|
231 | echo ' <tr>'
|
232 | echo ' <td></td>'
|
233 | for src_file in src/*; do
|
234 | echo "<td>$(basename $src_file)</td>"
|
235 | done
|
236 | echo ' </tr>'
|
237 | echo ' </thead>'
|
238 |
|
239 | echo ' <tr>'
|
240 | echo ' <td>source</td>'
|
241 | for src_file in src/*; do
|
242 | echo '<td><pre>'
|
243 | cat $src_file | html-escape
|
244 | echo '</pre></td>'
|
245 | done
|
246 | echo ' </tr>'
|
247 |
|
248 | echo ' <tr>'
|
249 | echo ' <td>output</td>'
|
250 | for out_file in output/*; do
|
251 | echo '<td><pre>'
|
252 | cat $out_file | html-escape
|
253 | echo '</pre></td>'
|
254 | done
|
255 | echo ' </tr>'
|
256 |
|
257 | echo '</table>'
|
258 | }
|
259 |
|
260 |
|
261 | _html-all() {
|
262 | html-head --title 'Shell vs. Shell' \
|
263 | ../web/base.css ../web/shell-vs-shell.css ../web/language.css
|
264 |
|
265 | echo '<body class="width50">'
|
266 |
|
267 | cmark <<EOF
|
268 | # Shell Design Comparison
|
269 |
|
270 | This is a friendly comparison of the syntax of different shells!
|
271 |
|
272 | - Oil: <https://github.com/oilshell/oil>
|
273 | - [A Tour of the Oil
|
274 | Language](https://www.oilshell.org/release/latest/doc/oil-language-tour.html)
|
275 | - Shell++: <https://github.com/alexst07/shell-plus-plus>
|
276 | - [Shell++ Language Basics](https://alexst07.github.io/shell-plus-plus/lang-basics/)
|
277 |
|
278 |
|
279 |
|
280 | - More shells: <https://github.com/oilshell/oil/wiki/Alternative-Shells>
|
281 | - Script that generates this file:
|
282 | <https://github.com/oilshell/oil/blob/master/test/shell-vs-shell.sh>
|
283 |
|
284 |
|
285 |
|
286 | EOF
|
287 |
|
288 | for dir in $BASE_DIR/CASE-*; do
|
289 | pushd $dir >/dev/null
|
290 |
|
291 | make-table
|
292 |
|
293 | popd >/dev/null
|
294 | done
|
295 |
|
296 | html-footer
|
297 | }
|
298 |
|
299 | html-all() {
|
300 | mkdir -p $BASE_DIR
|
301 |
|
302 | local out=$BASE_DIR/index.html
|
303 |
|
304 | _html-all | highlight-code > $out
|
305 |
|
306 | echo "Wrote $out"
|
307 | }
|
308 |
|
309 | all() {
|
310 | test-all
|
311 | html-all
|
312 | }
|
313 |
|
314 | "$@"
|