aboutsummaryrefslogtreecommitdiff
path: root/lepa.ha
blob: 2f08d6e23e50eeee7226b8a78b061f4982c41078 (plain)
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
// 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();
};

Generated with cgit - Back to sebastiano.tronto.net