commit d5165f64c63b582d0e65e33a1f1f0f0644853669
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date: Mon, 2 May 2022 16:41:16 +0200
Initial commit
Diffstat:
A | README | | | 16 | ++++++++++++++++ |
A | example.txt | | | 9 | +++++++++ |
A | lepa.ha | | | 215 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 240 insertions(+), 0 deletions(-)
diff --git a/README b/README
@@ -0,0 +1,16 @@
+This simple program, which I wrote mainly as an excuse to try out the Hare
+programming language [1], reads a list of letter pairs from a file, then
+prompts the user for commands. The interface is similar to that of ed(1).
+The main command 'r' starts a random training session, which consists of
+the program printing a random letter pair, waiting for the use to type
+enter, print the text associated with the letter pair, waiting again and
+repeating until EOF (Ctrl+D) is encountered.
+
+To try it out run:
+
+hare run lepa.ha example.txt
+
+Then type 'r' followed by Enter to start the random training session.
+Other commands include '?' (help) and 'q' (quit).
+
+[1] https://harelang.org
diff --git a/example.txt b/example.txt
@@ -0,0 +1,9 @@
+# This line is a comment. Comments and white lines are ignored.
+
+AB Abacus
+BB BB King
+CG Cigarette
+HR Hare
+BD Beedi (TODO: change this, too similar to CG)
+
+# And so on...
diff --git a/lepa.ha b/lepa.ha
@@ -0,0 +1,215 @@
+// Usage: lepa FILE
+// This is a simple program that reads from FILE lines starting with a letter
+// pair, that is a word consisting of two capital letters such as XY, saves
+// them in a table and lets the user access and visualize them in different
+// ways. It is intended to be a tool to train a memory scheme for letter pair
+// (e.g. for memory sports), but I mostly wrote it to try out Hare.
+
+use ascii;
+use bufio;
+use encoding::utf8;
+use fmt;
+use fs;
+use io;
+use math::random;
+use os;
+use strings;
+use time;
+
+type notlp = !void;
+
+def N: u32 = 26;
+let table: [N][N]str = [[""...]...];
+const commands: [_](str, *fn() void, str) = [
+ ("r", &dorand, "Start random training session"),
+ ("?", &printhelp, "Print help"),
+ ("q", &quit, "Quit"),
+];
+
+// Start random training session
+fn dorand() void = {
+ let pairs: [N*N](u32, u32) = [(0, 0)...];
+ let k = makeallpairs(pairs[0..N*N]);
+ let ran = math::random::init(time::unix(time::now(0)): u64);
+ for (true) {
+ const p = pairs[math::random::u32n(&ran, k)];
+ fmt::printfln("{}", pairstr(p))!;
+ if (waitline()) return;
+ fmt::printfln("{}", table[p.0][p.1])!;
+ if (waitline()) return;
+ };
+};
+
+// Print help
+fn printhelp() void = {
+ fmt::printfln("Available commands:")!;
+ for (let i: size = 0; i < len(commands); i += 1)
+ fmt::printfln("{}\t{}", commands[i].0, commands[i].2)!;
+};
+
+// Quit
+fn quit() void = os::exit(0);
+
+fn mainloop() void = {
+ for (true) {
+ let line: str = match(bufio::scanline(os::stdin)) {
+ case io::error =>
+ fmt::fatal("Error during io");
+ case io::EOF =>
+ fmt::printfln("^D")!;
+ os::exit(0);
+ case let line: []u8 =>
+ yield strings::fromutf8(line);
+ };
+ let i: size = 0;
+ for (i < len(commands); i += 1) {
+ if (line == commands[i].0) {
+ commands[i].1();
+ break;
+ };
+ };
+ if (i == len(commands))
+ fmt::printfln("?")!;
+ free(line);
+ };
+};
+
+// Determines if c is one of the N characters declared as letters
+fn isvalidle(c: rune) bool = ascii::isupper(c);
+
+// Index corresponding to character c in the table
+fn index(c: rune) u32 = (c: u32) - ('A': u32);
+
+// Character corresponding to index i in the table
+fn rundex(i: u32) rune = (i + ('A': u32)): rune;
+
+// Rune to string
+// TODO is there really no standard library function for this?
+fn runetostring(r: rune) str = {
+ let arr: []u8 = encoding::utf8::encoderune(r);
+ return strings::fromutf8(arr);
+};
+
+// Pair of indexes to string
+fn pairstr(p: (u32, u32)) str = {
+ const s1 = strings::dup(runetostring(rundex(p.0: u32)));
+ const s2 = strings::dup(runetostring(rundex(p.1: u32)));
+ return strings::concat(s1, s2);
+};
+
+// Read the table of letter pairs from file
+fn read(file: (io::file | fs::error)) void = {
+ const f = match(file) {
+ case let f: io::file =>
+ yield f;
+ case let e: fs::error =>
+ fmt::fatal("Error reading file: {}", fs::strerror(e));
+ };
+ const lines = match(io::drain(f)) {
+ case let lines: []u8 =>
+ yield strings::fromutf8(lines);
+ case io::error =>
+ fmt::fatal("Error reading table");
+ };
+ const lines = strings::split(lines, "\n");
+
+ for (let i = 0z; i < len(lines); i += 1) {
+ const runes: []rune = strings::runes(lines[i]);
+ if (len(lines[i]) == 0 || runes[0] == '#')
+ continue;
+ const c1 = runes[0];
+ const c2 = runes[1];
+ if (!(isvalidle(c1) && isvalidle(c2)))
+ fmt::fatal("Error reading line: {}", lines[i]);
+ let j: size = 2;
+ for (j < len(runes) && ascii::isspace(runes[j]))
+ j += 1;
+ const word: str = strings::sub(lines[i], j, strings::end);
+ if (len(word) > 0)
+ table[index(c1)][index(c2)] = strings::dup(word);
+ free(runes);
+ };
+ free(lines);
+};
+
+// Make list of all non-empty pairs
+fn makeallpairs(allpairs: [](u32, u32)) u32 = {
+ let k: u32 = 0;
+ for (let i: u32 = 0; i < N; i += 1) {
+ for (let j: u32 = 0; j < N; j += 1) {
+ if (table[i][j] != "") {
+ allpairs[k] = (i, j);
+ k += 1;
+ };
+ };
+ };
+ return k;
+};
+
+// Print one entry of the tabl
+fn printentry(p: (str | (u32, u32))) (void | notlp) = {
+ const ind: (u32, u32) = match(p) {
+ case let pair: (u32, u32) =>
+ yield pair;
+ case let s: str =>
+ yield if (len(s) != 2) {
+ return notlp;
+ } else {
+ const runes = strings::runes(s);
+ yield (index(runes[0]), index(runes[1]));
+ };
+ };
+ const i = ind.0;
+ const j = ind.1;
+ if (table[i][j] != "") {
+ const r1: rune = rundex(i: u32);
+ const r2: rune = rundex(j: u32);
+ fmt::printfln("{}{}:\t{}", r1, r2, table[i][j])!;
+ };
+};
+
+// Print one line of the table
+fn printline(c: (u32 | rune)) void = {
+ const i: u32 = match(c) {
+ case let i: u32 =>
+ yield i;
+ case let r: rune =>
+ yield index(r);
+ };
+ fmt::printfln("{}:", rundex(i))!;
+ for (let j: size = 0; j < len(table[i]); j += 1) {
+ // TODO: not ignore error?
+ printentry((i: u32, j: u32))!;
+ };
+};
+
+// Print all non-empty letter pairs
+fn printall() void = {
+ fmt::println("List of pairs:")!;
+ for (let i: size = 0; i < len(table); i += 1) {
+ printline(i: u32);
+ fmt::println("")!;
+ };
+};
+
+// Read a line from standard input, return true if EOF
+fn waitline() bool = {
+ match(bufio::scanline(os::stdin)) {
+ case io::error =>
+ fmt::fatal("Error during io");
+ case []u8 =>
+ return false;
+ case io::EOF =>
+ fmt::printfln("^D")!;
+ return true;
+ };
+};
+
+export fn main() void = {
+ if (len(os::args) != 2)
+ fmt::fatal("Usage: {} file", os::args[0]);
+
+ read(os::open(os::args[1]));
+
+ mainloop();
+};