1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
|
#!/usr/bin/env nomsu -V6.13.12.8
#
This is a Nomsu tutorial.
use "filesystem"
use "consolecolor"
use "commandline"
use "progressbar"
use "shell"
(lesson $name $lesson) compiles to ("
{name=\($name as lua expr), lesson=\(
quote ((SyntaxTree {.type = "FileChunks"} $lesson) as nomsu, text)
)}
")
[<your code here>, ???] all compile to:
at (this tree) fail "Incomplete code: This needs to be filled in."
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$lessons = [
lesson "Actions":
# Fix this action so the tests pass, then save and quit.
# If the tests don't pass, you can come back to this file later.
($x doubled) means ((???) * $x)
# Tests:
for $ in 1 to 10:
assume ($ doubled) == (2 * $)
lesson "Conditionals":
# Make this action return (yes) if its argument
# is bigger than 99, otherwise return (no)
($n is a big number) means:
if (<your code here>):
<your code here>
..else:
<your code here>
# Tests:
for $small_number in [0, 1, -5, -999, 99]:
assume ($small_number is a big number) == (no)
for $big_number in [9999, 100]:
assume ($big_number is a big number) == (yes)
lesson "Loops":
# Fix this action so the tests pass:
(the sum of $numbers) means:
$sum = 0
for $number in $numbers:
# Hint: math expressions may need parentheses
<your code here>
return $sum
# Tests:
assume (the sum of [1, 2, 3, 4, 5]) == 15
assume (the sum of [100, 200]) == 300
lesson "Variable Scopes":
# A nomsu variable that has not yet been assigned to is (nil)
assume $never_assigned == (nil)
# Nomsu's variables are local by default, and actions have their own scopes:
$x = 1
$y = 2
# Define an action that sets a variable:
(do something) means:
# The variable $y is never set in this action, so it has the same value
it has outside this action.
assume $y == (???)
# $x is set inside this action, and actions have their own scopes.
$x = $y
# What number should $x be here?
assume $x == (???)
# After running the action, what value should $x have?
do something
assume $x == (???)
lesson "More Variable Scopes":
# Loops and conditionals do *not* have their own scopes:
$z = 1
if (1 == 1):
# Set $z inside a conditional:
$z = 2
# After assigning in a conditional, what should $z be?
assume $z == (???)
for $ in 1 to 1:
# Set $z inside a loop:
$z = 3
# After assigning in a loop, what should $z be?
assume $z == (???)
lesson "Externals":
# The 'external' block lets you modify variables outside an action:
$x = 1
(do something) means:
external: $x = 2
do something
# After running the action that sets $x in an 'external' block, what should $x be?
assume $x == (???)
lesson "Locals":
# The 'with' block lets you create a local scope for the variables you list:
$y = 1
$z = 1
with [$y]:
$y = 2
$z = 2
# After setting $y and $z in the 'with [$y]' block, what should $y and $z be?
assume $y == (???)
assume $z == (???)
lesson "Failure and Recovery":
$what_happened = "nothing"
# In Nomsu, sometimes things fail, but you can recover from failures with 'try':
try:
# The 'fail' action triggers failure
fail "Oh no!"
..if it fails:
$what_happened = "failure"
..if it succeeds:
$what_happened = "success"
# What do you think happened?
assume $what_happened == (???)
# Note: a 'try' block will silence failures, so this has no effect:
try: fail
]
command line program with $args:
if ($args.help or $args.h):
say "Nomsu tutorial usage: nomsu -t tutorial [-x] [<location to put tutorial files>]"
exit
say
bold ("
+------------------------------------+
| Welcome to the Nomsu tutorial! |
+------------------------------------+
")
if ($args.extras is empty):
$tutorial_dir = "./nomsu_tutorial"
if (not ($Files.exists $tutorial_dir)):
when (ask "The Nomsu tutorial files will be in \(bold $tutorial_dir) is that okay? [Y/n] ") is:
"n" "N" "no":
say "Okay. If you want to specify another file location, run `nomsu -t tutorial <your location>`"
exit
..else:
$tutorial_dir = $args.extras.1
if (not ($Files.exists $tutorial_dir)):
sh> "mkdir \$tutorial_dir"
if ($args.x):
say ("
The tutorial files are located in \$tutorial_dir.
You're in charge of editing them yourself.
")
..else:
$EDITOR = (($os.getenv "EDITOR") or "nano")
say ("
The tutorial will use \(yellow $EDITOR) for editing files.
If you'd rather edit the files in another window, use the \(yellow "-x") flag.
e.g. \(yellow "nomsu -t tutorial -x tutorials/")
")
(filename of $i) means ("\$tutorial_dir/lesson\$i.nom", with "//" -> "/")
for $lesson in $lessons at $i:
$filename = (filename of $i)
unless ($Files.exists $filename):
write $lesson.lesson to file $filename
(get failures) means [
: for $lesson in $lessons at $i:
$filename = (filename of $i)
$file = (read file $filename)
$file = ($NomsuCode, from (Source $filename 1 (#$file)) $file)
try:
run $file
..if it fails with $msg:
$msg = ($msg, with "\n *stack traceback:.*" -> "")
add {.lesson_number = $i, .failure = $msg}
]
say ""
say (bold "Lessons:")
(show first failure from $failures) means:
say ("
\(bold "Next thing to fix:") \(
bold (yellow "Lesson \($failures.1.lesson_number): \($lessons.($failures.1.lesson_number).name)")
)
\($failures.1.failure, indented)
")
$failures = (get failures)
for $lesson in $lessons at $i:
for $f in $failures:
if ($f.lesson_number == $i):
say (bold (red " \$i. \($lesson.name) [incomplete]"))
do next $lesson
say (bold (green " \$i. \($lesson.name) [passed]"))
say "\n\(bold "Your progress:") \(20 wide (((#$lessons) - (#$failures)) / (#$lessons)) progress bar)"
repeat while (not ($failures is empty)):
show first failure from $failures
if ($args.x):
$filename = (filename of $failures.1.lesson_number)
say "\(yellow "Waiting for you to fix ")\(bold $filename) \(yellow "(press ctrl+c to exit)...")"
try:
$files = [: for $ in 1 to (#$lessons): add (read file (filename of $))]
repeat:
sh> "sleep 0.5"
$new_files = [: for $ in 1 to (#$lessons): add (read file (filename of $))]
if ($new_files != $files):
$files = $new_files
stop
..if it fails:
say "\nGoodbye."
exit
..else:
$filename = (filename of $failures.1.lesson_number)
--- (retry file) ---
when
ask (bold (cyan "Edit \$filename to get it to pass? [Y/n/exit] "))
..is:
"q" "quit" "exit" "n" "N" "no":
exit
"y" "Y" "yes" "":
$f = (read file $filename)
$cursor_positions = []
$cursor_positions, add ($f, position of "<your code here>" 1 (yes))
$cursor_positions, add ($f, position of "???" 1 (yes))
unless ($cursor_positions is empty):
$pos = (min of $cursor_positions)
[$line, $col] = [($f, line number at $pos), ($f, line position at $pos)]
when:
($EDITOR, matches "vim$"):
sh> "\$EDITOR \$filename '+call cursor(\$line,\$col)'"
($EDITOR, matches "nano$"):
sh> "\$EDITOR +\$line,\$col \$filename"
($EDITOR, matches "emacs$"):
sh> "\$EDITOR +\$line:\$col \$filename"
else:
sh> "\$EDITOR \$filename"
..else:
sh> "\$EDITOR \$filename"
else:
say "Sorry, I don't understand that."
go to (retry file)
$file = (read file $filename)
$file = ($NomsuCode, from (Source $filename 1 (#$file)) $file)
try:
run $file
..if it fails with $msg:
say (bold (red "\n There's a bit more to fix:"))
$msg = ($msg, with "\n *stack traceback:.*" -> "")
say ($msg, indented)
say ""
go to (retry file)
$prev_progress = (((#$lessons) - (#$failures)) / (#$lessons))
$failures = (get failures)
$progress = (((#$lessons) - (#$failures)) / (#$lessons))
if ($progress != $prev_progress):
if ($progress > $prev_progress):
say (bold (green "\nSuccess!\n"))
..else:
say (bold (red "\nUh oh, that broke something.\n"))
$N = 100
for $ in 0 to $N:
$k = (($ / $N) smoothed by 2)
$p = ($prev_progress to $progress mixed by $k)
say "\r\(bold "Your progress:") \(20 wide $p progress bar)" inline
$io.flush()
sh> "sleep \(1 / $N)"
say ""
say ("
\(bold "\(slow blink "Congratulations!")")
You've passed the tutorial!
\\(^ᴗ^)/
")
|