code / nomsu

Lines6.6K Lua5.1K PEG1.3K make117
2 others 83
Markdown60 Bourne Again Shell23
(561 lines)
1 #!/usr/bin/env nomsu -V7.0.0
2 ###
3 This is a Nomsu tutorial.
5 use "filesystem"
6 use "consolecolor"
7 use "commandline"
8 use "progressbar"
9 use "shell"
10 (lesson $name $lesson) compiles to ("
11 {name=\($name as lua expr), lesson=\(
12 quote ((SyntaxTree {.type = "FileChunks"} $lesson) as nomsu, text)
13 )}
14 ")
16 [<your code here>, ???] all compile to
17 \(
18 at \("Text" tree with "\((this tree).source)") fail
19 "Incomplete code: This needs to be filled in."
22 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 $lessons = [
25 lesson "Variables":
26 ### In Nomsu, variables have a "$" prefix, and you can just assign to them
27 without declaring them first:
28 $x = 1
29 test that ($x == 1)
31 ### Variables which have not yet been set have the value (nil)
32 test that ($not_yet_set == (nil))
34 ### Variables can be nameless:
35 $ = 99
37 ### Or have spaces, if surrounded with parentheses:
38 $(my favorite number) = 23
40 ### Figure out what value $my_var should have:
41 $my_var = 100
42 $my_var = ($my_var + $x + $(my favorite number))
43 test that ($my_var == (???))
45 lesson "Actions":
46 ### In Nomsu, actions are bits of code that run to produce a value or
47 do something. They have flexible syntax, so the values passed to
48 them can be interspersed with the action's name in any order.
49 ### Fix this action so the tests pass:
50 ($x doubled) means ((???) * $x)
52 ### Tests:
53 test that ((2 doubled) == 4)
54 test that ((-5 doubled) == -10)
56 lesson "Blocks":
57 ### When you need to do multiple things inside an action, use a block.
58 ### Blocks are written with a colon followed by some indented code:
59 ($x doubled then squared) means:
60 $x = (2 * $x)
61 $x = (???)
62 return $x
64 ### Blocks are also used for loops and conditionals:
65 for $num in [0, -1, 10, 4]:
66 $correct_answer = (4 * ($num * $num))
67 if (($num doubled then squared) != $correct_answer):
68 fail "Wrong answer for \($num)!"
70 lesson "Text":
71 ### Nomsu text is enclosed in double quotation marks:
72 $text = "Hello"
74 ### You can insert values into text using a backslash:
75 test that ("two plus three is \(2 + 3)" == (???))
77 ### Variables don't require parentheses, but other expressions do:
78 $x = 99
79 test that ("$x is \$x" == (???))
81 ### This can be used to convert values to text:
82 test that ("\$x" == (???))
84 ### Longer strings use '("' followed by an indented region:
85 $long = ("
86 line one
87 line two with spaces at the front
88 ")
90 test that
91 $long == ("
92 \(<your code here>)
93 \(<your code here>)
94 ")
96 lesson "Conditionals":
97 ### Make this action return "big" if its argument is bigger
98 than 99, otherwise return "small"
99 (the size of $n) means:
100 if (???):
101 <your code here>
102 ..else:
103 <your code here>
105 ### Tests:
106 for $big_number in [9999, 100]:
107 test that ((the size of $big_number) == "big")
109 for $small_number in [0, 1, -5, -999, 99]:
110 test that ((the size of $small_number) == "small")
112 lesson "Loops":
113 ### Fix this action so the tests pass:
114 (the sum of $numbers) means:
115 $sum = 0
117 ### You can loop over the values in a list like this:
118 for $number in $numbers:
119 ### Hint: math expressions may need parentheses
120 <your code here>
121 return $sum
123 ### Tests:
124 test that ((the sum of [1, 2, 3, 4, 5]) == 15)
125 test that ((the sum of [100, 200]) == 300)
127 ### You can also loop over a number range like this:
128 $total = 0
129 for $i in (1 to 3):
130 $total = ($total + $i)
131 test that ($total == (???))
133 lesson "Variable Scopes":
134 ### Nomsu's variables are local by default, and actions have their own scopes:
135 $x = 1
136 $y = 2
138 ### Define an action that sets a variable:
139 (do something) means:
140 ### The variable $y is never set in this action, so it has the same value
141 it has outside this action.
142 test that ($y == (???))
144 ### $x is set inside this action, and actions have their own scopes.
145 $x = $y
147 ### What number should $x be here?
148 test that ($x == (???))
150 ### After running the action, what value should $x have?
151 do something
152 test that ($x == (???))
154 lesson "More Variable Scopes":
155 ### Loops and conditionals do *not* have their own scopes:
156 $z = 1
157 if (1 == 1):
158 ### Set $z inside a conditional:
159 $z = 2
161 ### After assigning in a conditional, what should $z be?
162 test that ($z == (???))
163 for $ in (1 to 1):
164 ### Set $z inside a loop:
165 $z = 3
167 ### After assigning in a loop, what should $z be?
168 test that ($z == (???))
170 lesson "Externals":
171 ### The 'external' block lets you modify variables outside an action:
172 $x = 1
173 (do something) means:
174 external: $x = 2
175 do something
177 ### After running the action that sets $x in an 'external' block, what should $x be?
178 test that ($x == (???))
180 lesson "Locals":
181 ### The 'with' block lets you create a local scope for the variables you list:
182 $y = 1
183 $z = 1
184 with [$y]:
185 $y = 2
186 $z = 2
188 ### After setting $y and $z in the 'with [$y]' block, what should $y and $z be?
189 test that ($y == (???))
190 test that ($z == (???))
192 lesson "Failure and Recovery":
193 $what_happened = "nothing"
195 ### In Nomsu, sometimes things fail, but you can recover from failures with 'try':
196 try:
197 ### The 'fail' action triggers failure
198 fail "Oh no!"
199 ..if it fails:
200 $what_happened = "failure"
201 ..if it succeeds:
202 $what_happened = "success"
204 ### What do you think happened?
205 test that ($what_happened == (???))
207 ### Note: a 'try' block will silence failures, so this has no effect:
208 try: fail
210 lesson "Indexing":
211 ### Nomsu uses the "." operator to access things inside an object:
212 $dictionary = {.dog = "A lovable doofus", .cat = "An internet superstar"}
213 test that ($dictionary.dog == "A lovable doofus")
214 test that ($dictionary.cat == (???))
216 ### If you try to access a key that's not in an object, the result is (nil):
217 test that ($dictionary.mimsy == (???))
219 ### $dictionary.dog is just a shorthand for $dictionary."dog".
220 You may need to use the longer form for strings with spaces:
221 $dictionary."guinea pig" = "A real-life tribble"
223 ### Dictionaries are created with curly braces ({}) and can have
224 anything as a key or value, including numbers or other dictionaries:
225 $dictionary.5 = "The number five"
226 $dictionary.five = 5
227 $dictionary.myself = $dictionary
228 test that ($dictionary.myself == (???))
230 ### Lists are similar, but use square brackets ([])
231 and can only have numbers as keys, starting at 1:
232 $list = ["first", "second", 999]
233 test that ($list.1 == "first")
234 test that ($list.2 == (???))
235 test that ($list.3 == (???))
237 ### Hint: 4 should be a missing key
238 test that ($list.4 == (???))
239 test that ($list.foobar == (???))
241 ### The "#" action gets the number of items inside something:
242 test that (#$list == (???))
243 test that (#{.x = 10, .y = 20} == (???))
245 lesson "Methods":
246 ### The "," is used for method calls, which means calling an action
247 that's stored on an object (with the object as the first argument).
248 ### Lists have an "add" method that puts new items at the end:
249 $list = [-4, -6, 5]
250 $list, add 3
251 test that ($list == [-4, -6, 5, 3])
252 $list, add 7
253 test that ($list == [???])
255 ### Text also has some methods like:
256 $name = "Harry Tuttle"
257 test that (($name, from 7 to 12) == "Tuttle")
258 test that (($name, with "Tuttle" -> "Buttle") == (???))
260 ### Methods can be chained too:
261 test that (($name, with "Tuttle" -> "Buttle", from 7 to 12) == (???))
263 lesson "Object Oriented Programming":
264 ### Object Oriented Programming deals with things that have
265 associated values and behaviors.
266 ### Here, we define a Buffer to be a thing that has a .bits value:
267 (a Buffer) is (a thing) with [$bits]:
268 ### And some methods:
269 ($self, set up) means:
270 ### This method runs when a new buffer is created
271 $bits = []
273 ### This method is used to add to a buffer
274 ($self, add $bit) means:
275 $bits, add $bit
277 ### ($list, joined) is a list method that concatenates the list items:
278 ($self, as text) means ($bits, joined)
280 ### Write a method called ($self, length) that returns the sum
281 of the lengths of each bit in the buffer:
282 <your code here>
284 ### Create an instance of a Buffer:
285 $b = (a Buffer)
286 test that ($b is "a Buffer")
287 test that ((type of $b) is "a Buffer")
288 $b, add "xx"
289 $b, add "yyy"
290 test that (($b, as text) == "xxyyy")
291 test that (($b, length) == 5)
293 ### You can define a thing that inherits the behaviors of another thing like this:
294 (a Backwards Buffer) is (a Buffer) with [$bits]:
295 ### ($list, reversed) is a method that returns a copy of $list with
296 the order of the items reversed.
297 ($self, as text) means ($bits, reversed, joined)
298 $bb = (a Backwards Buffer)
299 $bb, add "one"
300 $bb, add "two"
301 test that (($bb, length) == (???))
302 test that (($bb, as text) == (???))
304 lesson "Files Part 1":
305 ### Define an external action here:
306 external:
307 ### These will be used in the next lesson
308 $global_var = 23
309 ($x tripled) means:
310 <your code here>
312 test that ((5 tripled) == 15)
313 test that ((2 tripled) == 6)
315 lesson "Files Part 2":
316 ### 'use' is the action for importing from other files.
317 ### It takes the path to the file (without the .nom extension):
318 use (<prev lesson>)
319 test that ((10 tripled) == (???))
320 test that ($global_var == (???))
322 $(ask normally) = $(ask)
323 command line program with $args:
324 if ($args.help or $args.h):
325 say ("
326 Nomsu tutorial usage: nomsu -t tutorial [-x] [<location to put tutorial files>]
328 exit
330 say
331 bold ("
333 +------------------------------------+
334 | Welcome to the Nomsu tutorial! |
335 +------------------------------------+
339 ### For this tutorial questions are hilighted in bold cyan:
340 (ask $q) means (ask normally (bold (cyan $q)))
342 ### Find the tutorial file directory:
343 if ($args.extras is empty):
344 $tutorial_dir = "./nomsu_tutorial"
345 unless ($Files.exists $tutorial_dir):
346 when
347 ask "The Nomsu tutorial files will be in \$tutorial_dir is that okay? [Y/n/exit] "
348 ..is:
349 "n" "N" "no":
350 $tutorial_dir = (ask "Where do you want to put the tutorial? ")
351 "exit" "quit" "q" "e": exit
352 ..else:
353 $tutorial_dir = $args.extras.1
355 ### Set up the tutorial file directory:
356 if (not ($Files.exists $tutorial_dir)):
357 make directory $tutorial_dir
358 (filename of $i) means ("\($tutorial_dir)/lesson\$i.nom", with "//" -> "/")
359 for ($i = $lesson) in $lessons:
360 $filename = (filename of $i)
361 unless ($Files.exists $filename):
362 $lesson_text =
363 $lesson.lesson, with "%(<prev lesson>%)" ->
364 "\"\(filename of ($i - 1), with "%.nom$" -> "", with "^%./" -> "")\""
366 write $lesson_text to file $filename
368 ### Display info about editing the tutorial files:
369 if $args.x:
370 say ("
371 The tutorial files are located in \$tutorial_dir.
372 You're in charge of editing them yourself.
374 ..else:
375 $EDITOR = ($os.getenv "EDITOR")
376 if $EDITOR:
377 when
378 ask ("
379 \$EDITOR is your system editor, would you like this tutorial to use it? [Y/n] \;
381 ..is:
382 "n" "N" "no":
383 $EDITOR = (nil)
385 unless $EDITOR:
386 say
387 $EDITOR =
388 ask ("
389 What program would you like to use to edit tutorial files?
390 (leave blank if you want to edit on your own in a different window)
391 > \;
394 if ($EDITOR == ""):
395 $EDITOR = (nil)
397 say ("
399 For this tutorial, a set of files has been created in \$tutorial_dir.
400 Each file corresponds to a lesson. Your task is to fix each file
401 so that it passes the tests.
405 (run lesson $i) means:
406 $filename = (filename of $i)
407 $file = (read file $filename)
408 $file = ($NomsuCode, from (Source $filename 1 #$file) $file)
409 $tree = ($file parsed)
410 $tree =
411 $tree, with
412 $ ->:
413 if ($ == \(<prev lesson>)):
414 return ("Text" tree with (filename of ($i - 1), with "%.nom$" -> ""))
416 run $tree
418 (get failures) means [:
419 for ($i = $lesson) in $lessons:
420 try:
421 run lesson $i
422 ..if it fails with $msg:
423 $msg = ($msg, with "\n *stack traceback:.*" -> "")
424 add {.lesson_number = $i, .failure = $msg}
427 say (bold "Lessons:")
428 (show first failure from $failures) means:
429 say ("
431 \(bold "Next thing to fix:") \(
432 bold
433 yellow ("
434 Lesson \($failures.1.lesson_number): \($lessons.($failures.1.lesson_number).name)
438 \($failures.1.failure, indented)
441 $failures = (get failures)
442 $current_lesson = (nil)
443 for ($i = $lesson) in $lessons:
444 for $f in $failures:
445 if ($f.lesson_number == $i):
446 $color = ((red) if $current_lesson else (yellow))
447 $current_lesson or= $i
448 say "\$color - \(bold "\$color\$i. \($lesson.name) [incomplete]")"
449 do next $lesson
451 say "\(green " + ")\(bold (green "\$i. \($lesson.name) [passed]"))"
453 if $(COLOR ENABLED):
454 say ("
456 \(bold "Your progress:") \(20 wide ((#$lessons - #$failures) / #$lessons) progress bar)
458 ..else:
459 say
460 say ((#$lessons - #$failures) / #$lessons progress bar)
462 repeat until ($failures is empty):
463 show first failure from $failures
465 ### Have the user fix the first failure:
466 unless $EDITOR:
467 ### If the user is using an external editor, wait for the file to change
468 $filename = (filename of $failures.1.lesson_number)
469 say ("
470 \(yellow "Waiting for you to fix ")\(bold $filename) \
471 ..\(yellow "(press ctrl+c to exit)...")
474 try:
475 $files = [: for $ in (1 to #$lessons): add (read file (filename of $))]
476 repeat:
477 sleep for 0.5 seconds
478 $new_files = [: for $ in (1 to #$lessons): add (read file (filename of $))]
479 if ($new_files != $files):
480 $files = $new_files
481 stop
482 ..if it fails:
483 say "\nGoodbye."
484 exit
485 ..else:
486 ### If the user is using $EDITOR, launch it so they can edit the file:
487 $filename = (filename of $failures.1.lesson_number)
488 --- (retry file) ---
489 when (ask "Edit \$filename to get it to pass? [Y/n/exit] ") is:
490 "q" "quit" "exit" "n" "N" "no": exit
491 "c":
492 write "# cheater!\n" to file $filename
494 "y" "Y" "yes" "":
495 $f = (read file $filename)
496 [$line, $col] = ($failures.1.failure, match ":(%d+),(%d+)")
497 if ($line and $col):
498 when:
499 ($EDITOR, matches "vim$"):
500 sh> "\$EDITOR \$filename '+call cursor(\$line,\$col)'"
502 ($EDITOR, matches "nano$"):
503 sh> "\$EDITOR +\$line,\$col \$filename"
505 ($EDITOR, matches "emacs$"):
506 sh> "\$EDITOR +\$line:\$col \$filename"
508 else:
509 sh> "\$EDITOR \$filename"
510 ..else:
511 sh> "\$EDITOR \$filename"
513 else:
514 say "Sorry, I don't understand that."
515 go to (retry file)
517 try:
518 run lesson $failures.1.lesson_number
519 ..if it fails with $msg:
520 $failures.1.failure = $msg
521 say (bold (red "\n There's a bit more to fix:"))
522 $msg = ($msg, with "\n *stack traceback:.*" -> "")
523 say ($msg, indented)
524 say
525 go to (retry file)
527 $prev_progress = ((#$lessons - #$failures) / #$lessons)
528 $failures = (get failures)
529 $progress = ((#$lessons - #$failures) / #$lessons)
531 ### Update the progressbar if progess has changed:
532 if ($progress != $prev_progress):
533 if ($progress > $prev_progress):
534 say (bold (green "\nSuccess!\n"))
535 ..else:
536 say (bold (red "\nUh oh, that broke something.\n"))
538 if $(COLOR ENABLED):
539 $N = 100
540 for $ in (0 to $N):
541 $k = (($ / $N) smoothed by 2)
542 $p = ($prev_progress to $progress mixed by $k)
543 say "\r\(bold "Your progress:") \(20 wide $p progress bar)" inline
544 $io.flush()
545 sleep for (1 / $N) seconds
546 ..else:
547 say ((#$lessons - #$failures) / #$lessons progress bar)
549 say
551 ### All done, no more failures:
552 say ("
554 \(bold "\(slow blink "Congratulations!")")
556 You've passed the tutorial!
558 \\(^\("ᴗ" if $(COLOR ENABLED) else "_")^)/