Working on refactoring kjv.

This commit is contained in:
Neil 2023-03-22 01:11:56 -07:00
parent ac983acf22
commit 7c8f0a63e9
10 changed files with 101 additions and 158 deletions

View File

@ -39,8 +39,8 @@ default: $(projects)
bin/test-text: build/text.o build/test_text.o
bin/test-journal: build/text.o build/journal.o build/test_journal.o
bin/test-source: build/text.o build/pair.o build/journal.o build/source.o build/test_source.o
bin/test-kjv: build/text.o build/pair.o build/kjvcount.o build/test_kjv.o
bin/kjv: build/text.o build/pair.o build/journal.o build/kjvcount.o build/kjv.o build/source.o
bin/test-kjv: build/text.o build/pair.o build/kjvcite.o build/test_kjv.o
bin/kjv: build/text.o build/pair.o build/journal.o build/kjvcite.o build/kjv.o build/source.o
bin/flight: build/text.o build/pair.o build/journal.o build/source.o build/flights.o build/flighthours.o
bin/%:
@ -71,7 +71,7 @@ build/%.c: src/%.re.c
# # https://github.com/neil-edelman/cdoc documentation
# -cdoc -o $@ $<
.SECONDARY: build/kjv.c build/journal.c build/source.c build/scan_kjv.c build/flights.c build/kjvcount.c
.SECONDARY: build/kjv.c build/journal.c build/source.c build/scan_kjv.c build/flights.c build/kjvcite.c
.PHONY: clean release test
test: $(projects)

View File

@ -33,10 +33,8 @@
@param[ARRAY_HEAD, ARRAY_BODY]
These go together to allow exporting non-static data between compilation units
by separating the `ARRAY_HEAD`, which is intended to go in the header, with
`ARRAY_NAME` and `ARRAY_TYPE`, and `ARRAY_BODY` functions. [All static
functions will have `s_` prepended for ease of creating a public thunk
functions, which most likely will conflict with static functions.]
by separating the header head from the code body. `ARRAY_HEAD` needs identical
`ARRAY_NAME` and `ARRAY_TYPE`.
@std C89 */
@ -53,8 +51,8 @@
|| defined(ARRAY_TRAIT) && !defined(ARRAY_HAS_TO_STRING))
#error Test requires to string.
#endif
#if defined ARRAY_HEAD && defined ARRAY_BODY
#error Can not be ARRAY_HEAD and ARRAY_BODY.
#if defined ARRAY_HEAD && (defined ARRAY_BODY || defined ARRAY_TRAIT)
#error Can not be simultaneously defined.
#endif
#ifndef ARRAY_H /* <!-- idempotent */

View File

@ -1,4 +1,4 @@
#include "kjvcount.h"
#include "kjvcite.h"
#define TREE_NAME kjvline
#define TREE_KEY union line64
@ -6,20 +6,8 @@
#define TREE_HEAD
#include "../src/tree.h"
#if defined PROTO \
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- proto */
#ifdef BASE
#error BASE!!!
#endif
/* fixme?? */
#include <stddef.h>
struct kjv {
struct kjvline_tree line;
struct kjvset_table set;
struct count_table count;
struct { size_t total, cumulative, set, verse; } words;
};
void kjv_line_(struct kjvline_tree *);
struct kjvline_tree kjv_line(struct journal *);
int kjv_line_is_empty(const struct kjvline_tree *);
@ -27,39 +15,3 @@ const char *kjv_line_to_string(const struct kjvline_tree *);
struct kjvline_tree_iterator kjv_line_iterator(struct kjvline_tree *);
int kjv_line_next(struct kjvline_tree_iterator *, union line64 *,
const struct kjvrange **);
#if 0
const char *kjvline_to_string(const struct kjvline *);
struct kjvline_iterator kjvline_iterator(struct kjvline *);
int kjvline_next(struct kjvline_iterator *, union line64 *,
const struct kjvline **);
struct flights_iterator { struct flight_tree_iterator _; };
struct flights flights(/*const*/ struct journal *);
void flights_(struct flights *);
const char *flights_to_string(const struct flights *);
struct flights_iterator flights_iterator(struct flights *);
int flights_next(struct flights_iterator *, union line64 *,
const struct flight **);
struct kjv kjv(void);
void kjv_(struct kjv *);
int kjv_is_empty(const struct kjv *const kjv);
/* Only used in test. */
int kjv_add(struct kjv *const kjv, const union kjvcite cite);
const char *kjv_to_string(const struct kjv *const kjv);
const char *kjv_set_to_string(const struct kjv *const kjv);
#endif
#endif /* proto --> */
#ifdef BASE
#undef BASE
#endif
#ifdef PRIVATE
#undef PRIVATE
#endif
#ifdef PROTO
#undef PROTO
#endif

View File

@ -233,24 +233,19 @@ int kjv_line_next(struct kjvline_tree_iterator *const it, union line64 *const k,
return 1;
}
int main(void) {
int success = EXIT_SUCCESS;
const char *reason = 0;
errno = 0;
struct kjvcount count = {0};
struct kjvcount_table count = {0};
struct journal jrnl = {0};
struct sources srcs = {0};
struct kjvline_tree lines = {0};
size_t no_total;
count = kjvcount();
fprintf(stderr, "KJV count: %s.\n", kjvcount_to_string(&count));
if(kjvcount_is_empty(&count))
{ reason = "kjv failed to load"; goto catch; }
count = kjv_count(&no_total);
fprintf(stderr, "KJV count: %s.\n", kjv_count_to_string(&count));
if(!no_total) { reason = "kjv failed to load"; goto catch; }
jrnl = journal();
fprintf(stderr, "Journal: %s.\n", journal_to_string(&jrnl));
@ -274,8 +269,7 @@ int main(void) {
printf("set term postscript eps enhanced\n"
"set output \"kjv.eps\"\n"
"$Data <<EOD\n"
"# date, source, verse, words, set / %zu\n",
count.words.total);
"# date, source, verse, words, set / %zu\n", no_total);
while(kjv_line_next(&it, &line, &range)) {
char citestr[12], datestr[12];
const struct source *src = source_lookup(&srcs, line); /* Source. */
@ -319,7 +313,7 @@ int main(void) {
"#set style fill solid 0.1 #pattern 5 (better, but restarts)\n"
"plot $Data using 1:($3)*100/%zu with fillsteps lw 2 title \"set\", \\\n"
"$Data using 1:($4)*100/%zu with steps lw 1 title \"cumulative\"\n",
count.words.total, count.words.total);
no_total, no_total);
goto finally;
catch:
success = EXIT_FAILURE;
@ -327,7 +321,7 @@ catch:
if(reason) fprintf(stderr, "Details: %s.\n", reason);
finally:
kjv_line_(&lines);
kjvcount_(&count);
kjv_count_(&count);
journal_(&jrnl);
return success;
}

View File

@ -89,7 +89,7 @@ void kjvcite_to_string(const union kjvcite, char (*)[12]);
#define TABLE_HEAD
#include "../src/table.h"
#define TABLE_NAME count
#define TABLE_NAME kjvcount
#define TABLE_KEY union kjvcite
#define TABLE_UINT uint32_t
#define TABLE_VALUE unsigned
@ -98,19 +98,12 @@ void kjvcite_to_string(const union kjvcite, char (*)[12]);
#include "../src/table.h"
#include <stddef.h> /* size_t */
struct kjvcount {
struct count_table verses;
struct { size_t total, cumulative, set; } words;
};
void kjvcount_(struct kjvcount *);
struct kjvcount kjvcount(void);
int kjvcount_is_empty(const struct kjvcount *);
const char *kjvcount_to_string(const struct kjvcount *);
void kjv_count_(struct kjvcount_table *);
struct kjvcount_table kjv_count(size_t *);
size_t kjv_count_get(struct kjvcount_table *, const union kjvcite);
const char *kjv_count_to_string(const struct kjvcount_table *);
/* FIXME: this should be in something else. */
struct kjvset_table kjv_set(void);
void kjv_set_(struct kjvset_table *);
int kjv_set_add(struct kjvset_table *const set,
struct kjvcount *const count, const union kjvcite cite);
enum table_result kjv_set_add(struct kjvset_table *, const union kjvcite);
const char *kjv_set_to_string(const struct kjvset_table *);

View File

@ -8,7 +8,7 @@
"All included Bible translations are in the public domain."
@std C11 */
#include "../src/kjvcount.h"
#include "../src/kjvcite.h"
#include "../src/text.h"
#include "../src/pair.h"
#include <inttypes.h>
@ -60,11 +60,12 @@ static void kjvset_to_string(const union kjvcite x, char (*const a)[12])
#include "../src/table.h"
/* Derived information on verse word count. */
static uint32_t count_hash(const union kjvcite x) { return kjvset_hash(x); }
static union kjvcite count_unhash(const uint32_t x) { return kjvset_unhash(x); }
static void count_to_string(const union kjvcite x, const unsigned count,
char (*const a)[12]) { (void)count; kjvset_to_string(x, a); }
#define TABLE_NAME count
static uint32_t kjvcount_hash(const union kjvcite x) { return kjvset_hash(x); }
static union kjvcite kjvcount_unhash(const uint32_t x)
{ return kjvset_unhash(x); }
static void kjvcount_to_string(const union kjvcite x, const unsigned count,
char (*const a)[12]) { (void)count; kjvcite_to_string(x, a); }
#define TABLE_NAME kjvcount
#define TABLE_KEY union kjvcite
#define TABLE_UINT uint32_t
#define TABLE_VALUE unsigned /* Count words. */
@ -144,27 +145,26 @@ scan:
*/
}
/** Frees `kjv`. */
void kjvcount_(struct kjvcount *const count) {
if(!count) return;
count_table_(&count->verses);
count->words.total = count->words.cumulative = count->words.set = 0;
}
/** Loads 66 files from the "kjv/" directory. Prints out something if it
doesn't work, but does not call `perror` or reset `errno`. Use
<fn:kjvcount_is_empty> to tell. */
struct kjvcount kjvcount(void) {
/* KJV count -- loaded up on initialization and is static for it's lifetime.
Given the verse citation, how many words does it have? */
/** Frees `count`. */
void kjv_count_(struct kjvcount_table *const count) { kjvcount_table_(count); }
/** Loads 66 files from the "kjv/" directory and counts all the words, which
are stored in `total`. `total` is zero if an error occurred, in which case the
details are sent to `stderr` and `errno` is set. @return On success, a
`kjvcount_table` that maps citations to word count. */
struct kjvcount_table kjv_count(size_t *const total) {
const char *const dir_kjv = "kjv";
struct char_array backing = text();
struct kjvcount count = {0};
struct kjvcount_table count = kjvcount_table();
DIR *dir = 0;
struct dirent *de = 0;
struct { size_t offset; int is; } build[KJV_BOOK_SIZE] = { 0 };
enum kjv_book b = 0;
int is_in_kjv = 0;
assert(total);
*total = 0;
/* For all files in directory KJV with <#>*.txt, read into backing. */
if(chdir(dir_kjv) == -1 || (is_in_kjv = 1, !(dir = opendir("."))))
goto catch;
@ -194,53 +194,39 @@ struct kjvcount kjvcount(void) {
const union kjvcite cite
= { .book = b, .chapter = x.chapter, .verse = x.verse };
unsigned *words;
switch(count_table_assign(&count.verses, cite, &words)) {
switch(kjvcount_table_assign(&count, cite, &words)) {
case TABLE_PRESENT: fprintf(stderr, "[%u]%s %u:%u duplicated.\n",
b + 1, kjv_book_string[b], x.chapter, x.verse); errno = EDOM;
case TABLE_ERROR: goto catch;
case TABLE_ABSENT: break;
}
*words = x.words, count.words.total += x.words;
*words = x.words, *total += x.words;
}
if(x.error) { fprintf(stderr, "[%u]%s on line %zu\n",
b + 1, kjv_book_string[b], x.line); goto catch; }
}
goto finally;
catch:
*total = 0;
if(de) fprintf(stderr, "While reading %s/%s.\n", dir_kjv, de->d_name);
else fprintf(stderr, "In directory %s/.\n", dir_kjv);
recatch:
kjvcount_(&count);
kjv_count_(&count);
finally:
if(dir) { if(closedir(dir)) { dir = 0; goto recatch; } dir = 0; }
if(is_in_kjv && (is_in_kjv = 0, chdir("..") == -1)) goto recatch;
text_(&backing);
return count;
}
size_t kjv_count_get(struct kjvcount_table *const count,
const union kjvcite cite) { return kjvcount_table_get(count, cite); }
const char *kjv_count_to_string(const struct kjvcount_table *const count)
{ return kjvcount_table_to_string(count); }
/** Has loaded properly? Otherwise, probably `errno` is set. */
int kjvcount_is_empty(const struct kjvcount *const kjv)
{ return !kjv || !kjv->verses.buckets; }
const char *kjvcount_to_string(const struct kjvcount *const count)
{ return count ? count_table_to_string(&count->verses) : ""; }
/* KJV set -- keeps track of membership by verse. */
struct kjvset_table kjv_set(void) { return kjvset_table(); }
void kjv_set_(struct kjvset_table *const set) { kjvset_table_(set); }
enum table_result kjv_set_add(struct kjvset_table *const set,
const union kjvcite cite) { return kjvset_table_try(set, cite); }
const char *kjv_set_to_string(const struct kjvset_table *const set)
{ return set ? kjvset_table_to_string(set) : 0; }
/** Adds `cite` to `kjv` if not present. Only used in test.
@return Is the kjv still valid. */
int kjv_set_add(struct kjvset_table *const set,
struct kjvcount *const count, const union kjvcite cite) {
size_t no_verse;
if(!set || !count) return 0;
no_verse = count_table_get(&count->verses, cite);
count->words.cumulative += no_verse;
switch(kjvset_table_try(set, cite)) {
case TABLE_ERROR: return 0;
case TABLE_ABSENT: count->words.set += no_verse; /* Sic. */
case TABLE_PRESENT: break;
}
return 1;
}

View File

@ -41,14 +41,14 @@
`TABLE_EXPECT_TRAIT` and then subsequently including the name in
`TABLE_TRAIT`.
@param[TABLE_HEAD, TABLE_BODY]
These go together to allow exporting non-static data between compilation units
by separating the `TABLE_BODY` refers to `TABLE_HEAD`, and identical
@param[TABLE_HEAD, TABLE_BODY]
These go together to allow exporting non-static data between compilation units
by separating the header head from the code body. `TABLE_HEAD` needs identical
`TABLE_NAME`, `TABLE_KEY`, `TABLE_UNHASH`, `TABLE_VALUE`, and `TABLE_UINT`.
@fixme Remove entry as public struct, this should be entirely private.
@fixme Why not have two `to_string` arguments on map? It's C, after all. This
would be useful in some practical cases.
@std C89 */
#if !defined(TABLE_NAME) || !defined(TABLE_KEY)
@ -61,8 +61,8 @@
|| defined(TABLE_TRAIT) && !defined(TABLE_HAS_TO_STRING))
#error Test requires to string.
#endif
#if defined TABLE_HEAD && defined TABLE_BODY
#error Can not be TABLE_HEAD and TABLE_BODY.
#if defined TABLE_HEAD && (defined TABLE_BODY || defined TABLE_TRAIT)
#error Can not be simultaneously defined.
#endif
#ifndef TABLE_H /* <!-- idempotent */
@ -94,10 +94,12 @@
![A diagram of the result states.](../doc/table/result.png) */
enum table_result { TABLE_RESULT };
#undef X
#ifndef TABLE_HEAD /* <!-- body */
#define X(n) #n
/** A static array of strings describing the <tag:table_result>. */
static const char *const table_result_str[] = { TABLE_RESULT };
#undef X
#endif /* body --> */
#undef TABLE_RESULT
#endif /* idempotent --> */

View File

@ -1,4 +1,5 @@
/** Dynamic contiguous string that is used to load files. */
/** Dynamic contiguous string that is used to load files into a
`struct char_array`. */
#include "text.h"
#include <stdio.h>
@ -8,14 +9,16 @@
#define ARRAY_BODY
#include "../src/array.h"
/** @return Idle. */
struct char_array text(void) { return char_array(); }
/** Destroys `text`. */
void text_(struct char_array *const text) { char_array_(text); }
/** Append a text file, `fn`, to `c`, and add a '\0'.
/** Append a text file, `fn`, to `text`, and add a '\0'.
@return The start of the appended file or null on error. A partial read is a
failure. @throws[fopen, fread, malloc]
@throws[EISEQ] The text file has embedded nulls.
@throws[ERANGE] If the standard library does not follow POSIX? */
failure. @throws[fopen, fread, realloc]
@throws[EISEQ] The text file has embedded nulls. */
char *text_append_file(struct char_array *text, const char *const fn) {
FILE *fp = 0;
const size_t granularity = 1024;

View File

@ -46,7 +46,7 @@
@param[TREE_HEAD, TREE_BODY]
These go together to allow exporting non-static data between compilation units
by separating the `TABLE_BODY` refers to `TABLE_HEAD`, and identical
by separating the header head from the code body. `TREE_HEAD` needs identical
`TREE_NAME`, `TREE_KEY`, `TREE_VALUE`, and `TREE_ORDER`.
@fixme merge, difference
@ -63,8 +63,8 @@
|| defined(TREE_TRAIT) && !defined(TREE_HAS_TO_STRING))
#error Test requires to string.
#endif
#if defined TREE_HEAD && defined TREE_BODY
#error Can not be TREE_HEAD and TREE_BODY.
#if defined TREE_HEAD && (defined TREE_BODY || defined TREE_TRAIT)
#error Can not be simultaneously defined.
#endif
#ifndef TREE_H /* <!-- idempotent */
@ -101,10 +101,12 @@
![A diagram of the result states.](../doc/tree/result.png) */
enum tree_result { TREE_RESULT };
#undef X
#ifndef TREE_HEAD /* <!-- body */
#define X(n) #n
/** A static array of strings describing the <tag:tree_result>. */
static const char *const tree_result_str[] = { TREE_RESULT };
#undef X
#endif /* body --> */
#undef TREE_RESULT
struct tree_node_count { size_t branches, leaves; };
#endif /* idempotent --> */

View File

@ -1,4 +1,4 @@
#include "../src/kjvcount.h"
#include "../src/kjvcite.h"
#include <stdio.h>
#include <inttypes.h> /* C99 */
#include <stdlib.h>
@ -8,25 +8,38 @@
int main(void) {
int success = EXIT_SUCCESS;
errno = 0; /* `errno` is not set correctly to 0 in some debug situations. */
struct kjvcount count = kjvcount();
size_t no_total, no_set = 0;
struct kjvcount_table count = kjv_count(&no_total);
struct kjvset_table set = kjv_set();
union kjvcite verse = { .book = Genesis, .chapter = 1, .verse = 1 };
fprintf(stderr, "%zu total words, %s.\n",
count.words.total, kjvcount_to_string(&count));
if(kjvcount_is_empty(&count)) goto catch;
kjv_set_add(&set, &count,
(union kjvcite){ .book = Genesis, .chapter = 1, .verse = 1 });
kjv_set_add(&set, &count,
(union kjvcite){ .book = Genesis, .chapter = 1, .verse = 2 });
kjv_set_add(&set, &count,
(union kjvcite){ .book = Genesis, .chapter = 1, .verse = 1 });
fprintf(stderr, "%zu of which: %s.\n", count.words.set, kjv_set_to_string(&set));
assert(count.words.total == 789633);
assert(count.words.set == 39);
no_total, kjv_count_to_string(&count));
if(!no_total) goto catch;
switch(kjv_set_add(&set, verse)) {
case TABLE_ERROR: goto catch;
case TABLE_PRESENT: assert(0); break;
case TABLE_ABSENT: no_set += kjv_count_get(&count, verse); break;
}
verse.verse = 2;
switch(kjv_set_add(&set, verse)) {
case TABLE_ERROR: goto catch;
case TABLE_PRESENT: assert(0); break;
case TABLE_ABSENT: no_set += kjv_count_get(&count, verse); break;
}
verse.verse = 1;
switch(kjv_set_add(&set, verse)) {
case TABLE_ERROR: goto catch;
case TABLE_PRESENT: break;
case TABLE_ABSENT: assert(0); break;
}
fprintf(stderr, "%zu of which: %s.\n", no_set, kjv_set_to_string(&set));
assert(no_total == 789633);
assert(no_set == 39);
goto finally;
catch:
success = EXIT_FAILURE, perror("kjv");
finally:
kjv_set_(&set);
kjvcount_(&count);
kjv_count_(&count);
return success;
}