aboutsummaryrefslogtreecommitdiff
path: root/lepa.ha
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2022-05-02 16:41:16 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2022-05-02 16:41:16 +0200
commitd5165f64c63b582d0e65e33a1f1f0f0644853669 (patch)
tree52caeed4b333cd1e2fe24eff4fc95f5ff93a4107 /lepa.ha
downloadlepa-d5165f64c63b582d0e65e33a1f1f0f0644853669.tar.gz
lepa-d5165f64c63b582d0e65e33a1f1f0f0644853669.zip
Initial commit
Diffstat (limited to 'lepa.ha')
-rw-r--r--lepa.ha215
1 files changed, 215 insertions, 0 deletions
diff --git a/lepa.ha b/lepa.ha
new file mode 100644
index 0000000..2f08d6e
--- /dev/null
+++ b/lepa.ha
@@ -0,0 +1,215 @@
1// Usage: lepa FILE
2// This is a simple program that reads from FILE lines starting with a letter
3// pair, that is a word consisting of two capital letters such as XY, saves
4// them in a table and lets the user access and visualize them in different
5// ways. It is intended to be a tool to train a memory scheme for letter pair
6// (e.g. for memory sports), but I mostly wrote it to try out Hare.
7
8use ascii;
9use bufio;
10use encoding::utf8;
11use fmt;
12use fs;
13use io;
14use math::random;
15use os;
16use strings;
17use time;
18
19type notlp = !void;
20
21def N: u32 = 26;
22let table: [N][N]str = [[""...]...];
23const commands: [_](str, *fn() void, str) = [
24 ("r", &dorand, "Start random training session"),
25 ("?", &printhelp, "Print help"),
26 ("q", &quit, "Quit"),
27];
28
29// Start random training session
30fn dorand() void = {
31 let pairs: [N*N](u32, u32) = [(0, 0)...];
32 let k = makeallpairs(pairs[0..N*N]);
33 let ran = math::random::init(time::unix(time::now(0)): u64);
34 for (true) {
35 const p = pairs[math::random::u32n(&ran, k)];
36 fmt::printfln("{}", pairstr(p))!;
37 if (waitline()) return;
38 fmt::printfln("{}", table[p.0][p.1])!;
39 if (waitline()) return;
40 };
41};
42
43// Print help
44fn printhelp() void = {
45 fmt::printfln("Available commands:")!;
46 for (let i: size = 0; i < len(commands); i += 1)
47 fmt::printfln("{}\t{}", commands[i].0, commands[i].2)!;
48};
49
50// Quit
51fn quit() void = os::exit(0);
52
53fn mainloop() void = {
54 for (true) {
55 let line: str = match(bufio::scanline(os::stdin)) {
56 case io::error =>
57 fmt::fatal("Error during io");
58 case io::EOF =>
59 fmt::printfln("^D")!;
60 os::exit(0);
61 case let line: []u8 =>
62 yield strings::fromutf8(line);
63 };
64 let i: size = 0;
65 for (i < len(commands); i += 1) {
66 if (line == commands[i].0) {
67 commands[i].1();
68 break;
69 };
70 };
71 if (i == len(commands))
72 fmt::printfln("?")!;
73 free(line);
74 };
75};
76
77// Determines if c is one of the N characters declared as letters
78fn isvalidle(c: rune) bool = ascii::isupper(c);
79
80// Index corresponding to character c in the table
81fn index(c: rune) u32 = (c: u32) - ('A': u32);
82
83// Character corresponding to index i in the table
84fn rundex(i: u32) rune = (i + ('A': u32)): rune;
85
86// Rune to string
87// TODO is there really no standard library function for this?
88fn runetostring(r: rune) str = {
89 let arr: []u8 = encoding::utf8::encoderune(r);
90 return strings::fromutf8(arr);
91};
92
93// Pair of indexes to string
94fn pairstr(p: (u32, u32)) str = {
95 const s1 = strings::dup(runetostring(rundex(p.0: u32)));
96 const s2 = strings::dup(runetostring(rundex(p.1: u32)));
97 return strings::concat(s1, s2);
98};
99
100// Read the table of letter pairs from file
101fn read(file: (io::file | fs::error)) void = {
102 const f = match(file) {
103 case let f: io::file =>
104 yield f;
105 case let e: fs::error =>
106 fmt::fatal("Error reading file: {}", fs::strerror(e));
107 };
108 const lines = match(io::drain(f)) {
109 case let lines: []u8 =>
110 yield strings::fromutf8(lines);
111 case io::error =>
112 fmt::fatal("Error reading table");
113 };
114 const lines = strings::split(lines, "\n");
115
116 for (let i = 0z; i < len(lines); i += 1) {
117 const runes: []rune = strings::runes(lines[i]);
118 if (len(lines[i]) == 0 || runes[0] == '#')
119 continue;
120 const c1 = runes[0];
121 const c2 = runes[1];
122 if (!(isvalidle(c1) && isvalidle(c2)))
123 fmt::fatal("Error reading line: {}", lines[i]);
124 let j: size = 2;
125 for (j < len(runes) && ascii::isspace(runes[j]))
126 j += 1;
127 const word: str = strings::sub(lines[i], j, strings::end);
128 if (len(word) > 0)
129 table[index(c1)][index(c2)] = strings::dup(word);
130 free(runes);
131 };
132 free(lines);
133};
134
135// Make list of all non-empty pairs
136fn makeallpairs(allpairs: [](u32, u32)) u32 = {
137 let k: u32 = 0;
138 for (let i: u32 = 0; i < N; i += 1) {
139 for (let j: u32 = 0; j < N; j += 1) {
140 if (table[i][j] != "") {
141 allpairs[k] = (i, j);
142 k += 1;
143 };
144 };
145 };
146 return k;
147};
148
149// Print one entry of the tabl
150fn printentry(p: (str | (u32, u32))) (void | notlp) = {
151 const ind: (u32, u32) = match(p) {
152 case let pair: (u32, u32) =>
153 yield pair;
154 case let s: str =>
155 yield if (len(s) != 2) {
156 return notlp;
157 } else {
158 const runes = strings::runes(s);
159 yield (index(runes[0]), index(runes[1]));
160 };
161 };
162 const i = ind.0;
163 const j = ind.1;
164 if (table[i][j] != "") {
165 const r1: rune = rundex(i: u32);
166 const r2: rune = rundex(j: u32);
167 fmt::printfln("{}{}:\t{}", r1, r2, table[i][j])!;
168 };
169};
170
171// Print one line of the table
172fn printline(c: (u32 | rune)) void = {
173 const i: u32 = match(c) {
174 case let i: u32 =>
175 yield i;
176 case let r: rune =>
177 yield index(r);
178 };
179 fmt::printfln("{}:", rundex(i))!;
180 for (let j: size = 0; j < len(table[i]); j += 1) {
181 // TODO: not ignore error?
182 printentry((i: u32, j: u32))!;
183 };
184};
185
186// Print all non-empty letter pairs
187fn printall() void = {
188 fmt::println("List of pairs:")!;
189 for (let i: size = 0; i < len(table); i += 1) {
190 printline(i: u32);
191 fmt::println("")!;
192 };
193};
194
195// Read a line from standard input, return true if EOF
196fn waitline() bool = {
197 match(bufio::scanline(os::stdin)) {
198 case io::error =>
199 fmt::fatal("Error during io");
200 case []u8 =>
201 return false;
202 case io::EOF =>
203 fmt::printfln("^D")!;
204 return true;
205 };
206};
207
208export fn main() void = {
209 if (len(os::args) != 2)
210 fmt::fatal("Usage: {} file", os::args[0]);
211
212 read(os::open(os::args[1]));
213
214 mainloop();
215};

Generated with cgit - Back to sebastiano.tronto.net