1 # This is the Tomo koans program
3 _HELP := "tomo koans - A tutorial program for learning the Tomo programming language"
13 Lesson((./lessons/lesson-01-hello-world.tm), "Hello World", "Hello world\n"),
14 Lesson((./lessons/lesson-02-tests.tm), "Testing Code"),
15 Lesson((./lessons/lesson-03-variables.tm), "Variables"),
16 Lesson((./lessons/lesson-04-functions.tm), "Functions"),
17 Lesson((./lessons/lesson-05-basic-types.tm), "Basic Types"),
18 Lesson((./lessons/lesson-06-arrays.tm), "Arrays"),
19 Lesson((./lessons/lesson-07-optionals.tm), "Optionals"),
20 Lesson((./lessons/lesson-08-tables.tm), "Tables"),
21 Lesson((./lessons/lesson-09-text.tm), "Text"),
22 Lesson((./lessons/lesson-10-structs.tm), "Structs"),
23 Lesson((./lessons/lesson-11-enums.tm), "Enums"),
24 Lesson((./lessons/lesson-12-allocating.tm), "Allocating Memory"),
25 Lesson((./lessons/lesson-13-paths.tm), "File Paths"),
26 Lesson((./lessons/lesson-14-langs.tm), "Embedded Languages"),
27 Lesson((./lessons/lesson-15-min-max.tm), "Min and Max"),
28 Lesson((./lessons/lesson-16-reducers.tm), "Reducers"),
31 enum TestResult(NotRun, Success(output:Text), Error(err:Text), WrongOutput(actual:Text, expected:Text))
32 func print(result:TestResult)
37 @(b,u:Program Output:)
42 @(b,u:Program Errors:)
46 is WrongOutput(actual, expected)
48 @(b,u:Program Output:)
54 func is_success(result:TestResult -> Bool)
55 when result is Success return yes
58 struct Lesson(file:Path, description:Text, expected_output:Text?=none)
59 func get_result(l:Lesson -> TestResult)
60 result := $Shell"COLOR=1 tomo -O 0 $(l.file)".result()
61 if not result.succeeded()
62 return Error(Text.from_utf8(result.errors)!)
64 output := Text.from_utf8(result.output)!
65 if expected := l.expected_output
67 return WrongOutput(output, expected)
69 return Success(output)
72 _ := ask("\[2]Press Enter to continue...\[]", bold=no)
75 say("\x1b[2J\x1b[H", newline=no)
77 func summarize_tests(results:[TestResult], highlight:Path?=none)
85 for i,lesson in LESSONS
86 when results[i]! is Success
89 @(green,bold:$(Text(i).left_pad(2)): "$(lesson.description)" (passes))
94 @(dim:$(Text(i).left_pad(2)): "$(lesson.description)" (not yet attempted))
99 @(red:$(Text(i).left_pad(2)): "$(lesson.description)" (failing))
102 completed := Num(passing)/Num(passing+failing)
105 @(cyan,b:Progress: $(completed.percent()))
109 func short_summarize_tests(results:[TestResult])
110 say("Progress: ", newline=no)
111 for result in results
112 when result is Success
113 $Colorful"@(green,bold:#)".print(newline=no)
115 $Colorful"@(dim:#)".print(newline=no)
117 $Colorful"@(red:#)".print(newline=no)
121 func choose_option(options:{Text:Text} -> Text)
128 choice := (ask("Choose an option: ") or goodbye()).lower().to(1)
129 if options.has(choice)
131 else if choice == "q"
135 @(red:I'm sorry, I don't recognize that choice, please try again!")
139 func show_lesson(lesson:Lesson, result:TestResult)
143 @(yellow,b,u:$(lesson.description))
144 Here's what we have right now:
149 when result is Success
151 @(green,b:✨ Great job, this test is passing! ✨)
156 @(dim,italic:...nothing, edit the file to make your first attempt...)
161 @(red,b:Looks like this test isn't passing yet! 😢)
165 func goodbye(-> Abort)
169 @(b:Goodbye! Come back again soon!)
174 func give_feedback(section:Text)
176 user = user or ask("What name do go by? (optional) ") or "anon"
177 if user == "" then user = "anon"
178 feedback := ask("What's your feedback? ") or return
179 (./feedback.txt).append("
180 [$(user!)/$section] $feedback\n
183 func main(clean=no -> Abort)
187 @(bold,green:Hello and welcome to the Tomo Koans program!)
189 We're going to run through some programs that don't work
190 and you'll be fixing them up!
197 (./editor.txt).remove(ignore_missing=yes)!
198 (./lessons).remove(ignore_missing=yes)!
200 if (./editor.txt).exists()
201 editor = (./editor.txt).read()!
203 @(dim,i:You're using @(green:$(editor)) as your text editor. If you want to change it, just edit @(magenta:./editor.txt))
208 What command line text editor do you want to use?
210 Ones with syntax highlighting: vim, nvim, emacs
211 Simpler editors: nano, pico, edit
215 while editor == "" or not $Shell"command -v $editor >/dev/null".run().succeeded()
216 editor = ask("I don't recognize that editor. Try again? ") or goodbye()
217 (./editor.txt).write(editor)!
220 Great! From now on, I'll use @(b:$(editor)) to edit files.
221 If you want to change it, just edit @(magenta:./editor.txt)
226 cp -r lesson-templates lessons
227 ".run().or_fail("Could not make lessons directory")
229 test_results := &[TestResult.NotRun for l in LESSONS]
232 message_pending := no
234 unless message_pending
238 summarize_tests(test_results)
239 choice := ask("Choose a test, (q)uit, or give (f)eedback: ") or stop repeat
240 stop repeat if choice == "q" or choice == "Q"
242 if choice == "f" or choice == "F"
243 give_feedback("menu")
247 for i,result in test_results
248 if not result.is_success()
252 n := Int.parse(choice) or (do
253 $Colorful"@(red:I don't know what that means! Type a test number or 'q'.)".print()
254 message_pending = yes
258 if n < 1 or n > LESSONS.length
259 $Colorful"@(red:That's not a valid test number!)".print()
260 message_pending = yes
264 lesson := LESSONS[n]!
265 show_lesson(lesson, test_results[n]!)
266 short_summarize_tests(test_results)
269 "e": "Edit file and try again",
270 "l": "Show the lesson list",
272 "f": "Give Feedback",
274 if n < LESSONS.length
275 options["n"] = "Go to the next lesson"
277 when choose_option(options) is "e"
279 $(editor) $(lesson.file)
280 ".run().or_fail("Could not open editor $(editor)")
282 test_results[n] = lesson.get_result()
283 test_results[n]!.print()
291 give_feedback("lesson$n")