From 7c8f0a63e97a5b8bf9fa2a3ae618710a4ed2940e Mon Sep 17 00:00:00 2001 From: Neil Date: Wed, 22 Mar 2023 01:11:56 -0700 Subject: [PATCH] Working on refactoring kjv. --- Makefile | 6 +-- src/array.h | 10 ++-- src/kjv.h | 52 +------------------- src/kjv.re.c | 22 ++++----- src/{kjvcount.h => kjvcite.h} | 19 +++----- src/{kjvcount.re.c => kjvcite.re.c} | 74 ++++++++++++----------------- src/table.h | 14 +++--- src/text.c | 13 +++-- src/tree.h | 8 ++-- test/test_kjv.c | 41 ++++++++++------ 10 files changed, 101 insertions(+), 158 deletions(-) rename src/{kjvcount.h => kjvcite.h} (80%) rename src/{kjvcount.re.c => kjvcite.re.c} (79%) diff --git a/Makefile b/Makefile index 628d755..c6ab3b6 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/src/array.h b/src/array.h index 1b03c34..cd2f0a4 100644 --- a/src/array.h +++ b/src/array.h @@ -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 /* */ - -#ifdef BASE -#undef BASE -#endif -#ifdef PRIVATE -#undef PRIVATE -#endif -#ifdef PROTO -#undef PROTO -#endif diff --git a/src/kjv.re.c b/src/kjv.re.c index 4d8e701..1d04b80 100644 --- a/src/kjv.re.c +++ b/src/kjv.re.c @@ -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 < /* 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 *); diff --git a/src/kjvcount.re.c b/src/kjvcite.re.c similarity index 79% rename from src/kjvcount.re.c rename to src/kjvcite.re.c index e777b8c..b933d50 100644 --- a/src/kjvcount.re.c +++ b/src/kjvcite.re.c @@ -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 @@ -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 - 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; -} diff --git a/src/table.h b/src/table.h index c0b9a31..53a9c6d 100644 --- a/src/table.h +++ b/src/table.h @@ -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 /* */ #undef TABLE_RESULT #endif /* idempotent --> */ diff --git a/src/text.c b/src/text.c index 5283c8c..c2a1ea8 100644 --- a/src/text.c +++ b/src/text.c @@ -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 @@ -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; diff --git a/src/tree.h b/src/tree.h index 270044b..fffdbc5 100644 --- a/src/tree.h +++ b/src/tree.h @@ -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 /* */ #undef TREE_RESULT struct tree_node_count { size_t branches, leaves; }; #endif /* idempotent --> */ diff --git a/test/test_kjv.c b/test/test_kjv.c index 36b9204..1c40971 100644 --- a/test/test_kjv.c +++ b/test/test_kjv.c @@ -1,4 +1,4 @@ -#include "../src/kjvcount.h" +#include "../src/kjvcite.h" #include #include /* C99 */ #include @@ -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; }