diff --git a/Makefile b/Makefile index 7d7c43d..97e5b9b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -optimize := -ffast-math + optimize := -ffast-math warnbasic := -Wall -pedantic #-ansi # -std=c99 warnclang := -Wextra -Weverything \ -Wno-comma \ @@ -38,12 +38,9 @@ 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/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/score: build/text.o build/pair.o build/journal.o build/scores.o -bin/scan: build/text.o build/journal.o build/kjvcite.o build/pair.o build/scan.o build/driver.o +bin/test-source: build/text.o build/pair.o build/journal.o build/scan.o build/kjv.o build/test_source.o +bin/test-kjv: build/text.o build/pair.o build/kjv.o build/test_kjv.o +bin/scan: build/text.o build/journal.o build/kjv.o build/pair.o build/scan.o build/driver.o bin/%: @echo "\033[1;36mlinking $@\033[0m" diff --git a/src/flighthours.c b/src/flighthours.c deleted file mode 100644 index cd18f3b..0000000 --- a/src/flighthours.c +++ /dev/null @@ -1,103 +0,0 @@ -/** @license 2023 Neil Edelman, distributed under the terms of the - [MIT License](https://opensource.org/licenses/MIT). - - Date _vs_ hours flown. */ - -#include "flights.h" -#include "source.h" -#include -#include -#include -#include -#include - -int main(void) { - int success = EXIT_SUCCESS; - errno = 0; - struct journal j = journal("../journal"); - struct sources s = sources(&j); - struct flight_tree f = flights(&j); - - if(errno) goto catch; - fprintf(stderr, "Journal: %s.\n" - "Flights: %s.\n", journal_to_string(&j), flights_to_string(&f)); - printf("set terminal pngcairo dashed transparent truecolor" - " size 840, 480 fontscale 1\n" - "set output \"flight.png\"\n"); - /*printf("set terminal cairolatex standalone pdf size 16cm,10.5cm" - " dashed transparent\n" - "set output \"flight.tex\"\n");*/ - /*printf("set term postscript eps enhanced\n" - "set output \"flighthours.eps\"\n");*/ - printf("$Data <name.a) { errno = EDOM; goto catch; } - printf("%s, ", datestr); - assert(flight); switch(flight->type) { - case GLIDER: - printf("%.*s, %" PRIu32 ", %" PRIu32, - (int)(flight->glider.reg.b - flight->glider.reg.a), - flight->glider.reg.a, - flight->glider.dual_min, - flight->glider.pilot_min + flight->glider.instr_min); - break; - case POWER: - printf("%.*s, %" PRIu32 ", %" PRIu32, - (int)(flight->power.reg.b - flight->power.reg.a), - flight->power.reg.a, - flight->power.dual_min, - flight->power.pilot_min); - break; - } - printf(", %.*s\n", (int)(src->name.b - src->name.a), src->name.a); - } - printf("EOD\n" - "# theozh https://stackoverflow.com/a/75466214/2472827\n" - "# get a unique list from datablock\n" - "addToList(list,col) = list.( strstrt(list,'\"'.strcol(col).'\"') \\\n" - " > 0 ? '' : ' \"'.strcol(col).'\"')\n" - "Uniqs = ''\n" - "stats $Data u (Uniqs=addToList(Uniqs,2)) nooutput\n" - "Uniq(i) = word(Uniqs,i)\n" - "getIndex(s) = sum [_i=1:words(Uniqs)] s eq word(Uniqs,_i) ? _i : 0\n" - "\n" - "stats $Data u 3 nooutput\n" - "sicsum = STATS_sum\n" - "stats $Data u 4 nooutput\n" - "picsum = STATS_sum\n" - "\n" - "myTimeFmt = \"%%Y-%%m-%%d\"\n" - "set format x myTimeFmt timedate\n" - "set xtics format myTimeFmt rotate by -30\n" - "set format y \"%%tH:%%tM\" timedate\n" - "set grid\n" - "set key out reverse Left noautotitle\n" - "set style fill solid 0.5\n" - "unset border\n" - "plot total=0 $Data u" - " (timecolumn(1,myTimeFmt)):(dy=($3+$4)*60,total=total+dy)" - " w steps lc \"black\" dt 3, \\\n" - " total=0 '' u (timecolumn(1,myTimeFmt)):" - "(dy=($3+$4)*60,total=total+dy,total/2.): \\\n" - " (43200):(total/2.):(getIndex(strcol(2))) w boxxy lc var, \\\n" - " for [i=1:words(Uniqs)] keyentry w boxxy lc i ti Uniq(i)\n" - /*"set xrange [*:'2001-09-11']\n"*/ - /*"#set style fill solid 0.1 #pattern 5 (better, but restarts)\n" - "plot $Data using 1:($6/60) with fillsteps lw 2\n"*/); - goto finally; -catch: - success = EXIT_FAILURE; - perror("flights"); -finally: - flights_(&f); - sources_(&s); - journal_(&j); - return success; -} diff --git a/src/flights.h b/src/flights.h deleted file mode 100644 index 289396d..0000000 --- a/src/flights.h +++ /dev/null @@ -1,57 +0,0 @@ -#define LAUNCH_TYPE \ - X(MotorCarTow),\ - X(Winch),\ - X(AeroTow) -#define X(type) type -enum launch_type { LAUNCH_TYPE }; -#undef X -#define X(type) #type -static const char *launch_type_string[] = { LAUNCH_TYPE }; -#undef X -#undef LAUNCH_TYPE - -#define FLIGHT_TYPE \ - X(Glider),\ - X(Power) -#define X(type) type -enum flight_type { FLIGHT_TYPE }; -#undef X -#define X(type) #type -static const char *flight_type_string[] = { FLIGHT_TYPE }; -#undef X -#undef FLIGHT_TYPE - -#include "pair.h" - -struct glider { - struct pair type, reg, launch, landing; - enum launch_type how; - unsigned height_ft, pilot_min, dual_min, instr_min; - struct pair remarks; -}; - -struct power { - struct pair type, reg, launch, landing, pilot, copilot; - unsigned dual_min, pilot_min, ifrsim_min, ifr_min; - struct pair remarks; -}; - -struct flight { - enum { GLIDER, POWER } type; - union { struct glider glider; struct power power; }; -}; - -#include "../src/journal.h" /* line64 */ - -#define TREE_NAME flight -#define TREE_KEY union line64 -#define TREE_VALUE struct flight -#define TREE_HEAD -#include "../src/tree.h" - -struct flight_tree flights(/*const*/ struct journal *); -void flights_(struct flight_tree *); -const char *flights_to_string(const struct flight_tree *); -struct flight_tree_iterator flights_iterator(struct flight_tree *); -int flights_next(struct flight_tree_iterator *, union line64 *, - const struct flight **); diff --git a/src/flights.re.c b/src/flights.re.c deleted file mode 100644 index 282ab78..0000000 --- a/src/flights.re.c +++ /dev/null @@ -1,239 +0,0 @@ -/** @license 2023 Neil Edelman, distributed under the terms of the - [MIT License](https://opensource.org/licenses/MIT). - @std C11 */ -#include "../src/flights.h" -#include -#include -#include - - -static void flight_to_string(const union line64 line, const struct flight *u, - char (*const a)[12]) { (void)u; date32_to_string(line.date, a); } -static int flight_compare(const union line64 a, const union line64 b) - { return a.u64 > b.u64; } -#define TREE_NAME flight -#define TREE_KEY union line64 -#define TREE_VALUE struct flight -#define TREE_COMPARE -#define TREE_TO_STRING -#define TREE_BODY -#include "../src/tree.h" - - -/*!conditions:re2c*/ - -static int scan(struct flight_tree *const f, - union date32 date, const char *const text) { - const char *YYCURSOR, *YYMARKER, *yyt1, *yyt2, *s0, *s1, *t0, *t1; - enum YYCONDTYPE condition = yycline; - size_t line = 1; - char datestr[12] = {0}; - const char *why = "unexpected"; - struct flight *flight = 0; - assert(f && text); - YYCURSOR = YYMARKER = yyt1 = text; - /*!re2c /**/ - re2c:define:YYCTYPE = char; - re2c:yyfill:enable = 0; - re2c:define:YYGETCONDITION = "condition"; - re2c:define:YYSETCONDITION = "condition = @@;"; - re2c:define:YYGETCONDITION:naked = 1; - re2c:define:YYSETCONDITION:naked = 1; - - ws = [ \t]; - glyph = [^\x00-\x20\x7f]; // [^\x00\n\t ] + all weird - semitext = glyph \ ";"; - natural = [1-9][0-9]*; - minutes = [0-5][0-9]; - zero_natural = natural | "0"; - airport = [A-Z0-9]{4,4}; - */ - for( ; ; ) { /*!re2c /**/ - /* Default ignore. */ - [^\n\x00] { continue; } - "\x00" { why = "no newline at end of file"; goto catch; } - "\x00" { return 1; } - "\n" => line { line++; continue; } - * :=> skip - - /* Except this . . . */ - "[glider]" :=> glider_type - /* type, reg, launch, how, height, landing, pilot, dual, instr, remarks - eg, [glider] 2-33A; C-GCLK; CYQQ; A; 2000'; CYQQ; ;:13;; Peters D1 */ - * { why = "type unrecognized"; goto catch; } - * { why = "reg unrecognized"; goto catch; } - * { why = "launch unrecognized"; goto catch; } - * { why = "how unrecognized"; goto catch; } - * { why = "height unrecognized"; goto catch; } - * { why = "landing unrecognized"; goto catch; } - * { why = "pilot unrecognized"; goto catch; } - * { why = "dual unrecognized"; goto catch; } - * { why = "instr unrecognized"; goto catch; } - * { why = "remarks unrecognized"; goto catch; } - ws* @s0 semitext+ @s1 ws* ";" => glider_reg { - const union line64 key - = {{ (uint32_t)line, {{ date.day, date.month, date.year }} }}; - assert(!flight); - if(line > UINT32_MAX) { why = "line overflow"; goto catch; } - switch(flight_tree_assign(f, key, &flight)) { /* fixme */ - case TREE_PRESENT: why = "duplicate key"; - case TREE_ERROR: goto catch; - case TREE_ABSENT: flight->type = GLIDER; break; - } - flight->glider.type.a = s0, flight->glider.type.b = s1; - continue; - } - ws* @s0 semitext+ @s1 ws* ";" => glider_launch - { flight->glider.reg.a = s0, flight->glider.reg.b = s1; continue; } - ws* @s0 airport @s1 ws* ";" => glider_how - { flight->glider.launch.a = s0, flight->glider.launch.b = s1; - continue; } - ws* @s0 [MWA] ws* ";" => glider_height { - switch(*s0) { - case 'M': flight->glider.how = MotorCarTow; break; - case 'W': flight->glider.how = Winch; break; - case 'A': flight->glider.how = AeroTow; break; - default: assert(0); break; - } - continue; - } - ws* @s0 natural @s1 "'" ws* ";" => glider_landing - { if(!pair_to_natural(s0, s1, &flight->glider.height_ft)); - continue; } - ws* @s0 airport @s1 ws* ";" => glider_pilot - { flight->glider.landing.a = s0, flight->glider.landing.b = s1; - continue; } - ws* ";" => glider_dual /* not PIC */ - { flight->glider.pilot_min = 0; continue; } - ws* @s0 natural? @s1 ":" @t0 minutes @t1 ws* ";" - => glider_dual { if(!pair_colon_to_minutes(s0, s1, t0, t1, - &flight->glider.pilot_min)) { why = "pilot time"; goto catch; } - continue; } - ws* ";" => glider_instr - { flight->glider.dual_min = 0; continue; } - ws* @s0 natural? @s1 ":" @t0 minutes @t1 ws* ";" - => glider_instr { if(!pair_colon_to_minutes(s0, s1, t0, t1, - &flight->glider.dual_min)) { why = "dual time"; goto catch; } - continue; } - ws* ";" => glider_remarks - { flight->glider.instr_min = 0; continue; } - ws* @s0 natural? @s1 ":" @t0 minutes @t1 ws* ";" - => glider_remarks { if(!pair_hours_to_minutes(s0, s1, t0, t1, - &flight->glider.instr_min)) { why = "instr time"; goto catch; } - continue; } - ws* "\n" => line - { flight->glider.remarks.a = flight->glider.remarks.b = 0; - flight = 0; line++; continue; } - ws* @s0 glyph+ (ws+ glyph+)* @s1 "\n" => line - { flight->glider.remarks.a = s0, flight->glider.remarks.b = s1; - flight = 0; line++; continue; } - - /* And this . . . */ - "[flight]" :=> flight_type - /* type; registration; launch -- landing; pic; sic; - single engine day dual; pilot; instrument simulated; actual; remarks */ - * { why = "type unrecognized"; goto catch; } - * { why = "reg unrecognized"; goto catch; } - * { why = "airports unrecognized"; goto catch; } - * { why = "pic unrecognized"; goto catch; } - * { why = "sic unrecognized"; goto catch; } - * { why = "dual time unrecognized"; goto catch; } - * { why = "pilot time unrecognized"; goto catch; } - * { why = "simulated ifr time unrecognized"; - goto catch; } - * { why = "ifr time unrecognized"; goto catch; } - * { why = "remarks unrecognized"; goto catch; } - ws* @s0 semitext+ @s1 ws* ";" => flight_reg { - const union line64 key - = {{ (uint32_t)line, {{ date.day, date.month, date.year }} }}; - assert(!flight); - if(line > UINT32_MAX) { why = "line overflow"; goto catch; } - switch(flight_tree_assign(f, key, &flight)) { - case TREE_PRESENT: why = "duplicate key"; - case TREE_ERROR: goto catch; - case TREE_ABSENT: flight->type = POWER; break; - } - flight->power.type.a = s0, flight->power.type.b = s1; - continue; - } - ws* @s0 semitext+ @s1 ws* ";" => flight_airports - { flight->power.reg.a = s0, flight->power.reg.b = s1; continue; } - ws* @s0 airport @s1 ws* "--" - ws* @t0 airport @t1 ws* ";" => flight_pic { - flight->power.launch.a = s0, flight->power.launch.b = s1; - flight->power.landing.a = t0, flight->power.landing.b = t1; - continue; - } - ws* @s0 semitext+ (ws+ semitext+)* @s1 /* ws*? */";" - => flight_sic { flight->power.pilot.a = s0, - flight->power.pilot.b = s1; continue; } - ws* ";" => flight_dual - { flight->power.copilot.a = flight->power.copilot.b = 0; continue; } - ws* @s0 semitext+ (ws+ semitext+)* @s1 ";" - => flight_dual { flight->power.copilot.a = s0, - flight->power.copilot.b = s1; continue; } - ws* ";" => flight_pilot - { flight->power.dual_min = 0; continue; } - ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";" - => flight_pilot { if(!pair_hours_to_minutes(s0, s1, t0, t1, - &flight->power.dual_min)) { why = "dual time"; goto catch; } - continue; } - ws* ";" => flight_ifrsim - { flight->power.pilot_min = 0; continue; } - ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";" - => flight_ifrsim { if(!pair_hours_to_minutes(s0, s1, t0, t1, - &flight->power.pilot_min)) { why = "pilot time"; goto catch; } - continue; } - ws* ";" => flight_ifr - { flight->power.ifrsim_min = 0; continue; } - ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";" - => flight_ifr { if(!pair_hours_to_minutes(s0, s1, t0, t1, - &flight->power.ifrsim_min)) { why = "simulated ifr time"; - goto catch; } continue; } - ws* ";" => flight_remarks - { flight->power.ifr_min = 0; continue; } - ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";" - => flight_remarks { if(!pair_hours_to_minutes(s0, s1, t0, t1, - &flight->power.ifr_min)) { why = "pilot time"; goto catch; } - continue; } - ws* "\n" => line - { flight->power.remarks.a = flight->power.remarks.b = 0; - flight = 0; line++; continue; } - ws* @s0 glyph+ (ws+ glyph+)* @s1 "\n" => line - { flight->power.remarks.a = s0, flight->power.remarks.b = s1; - flight = 0; line++; continue; } - */ } - assert(0); /* Never gets here. */ -catch: - if(!errno) errno = EILSEQ; - date32_to_string(date, &datestr); - fprintf(stderr, "%s line %zu: %s.\n", datestr, line, why); - return 0; -} -void flights_(struct flight_tree *const f) { flight_tree_(f); } -struct flight_tree flights(struct journal *const j) { - struct flight_tree f = flight_tree(); - struct journal_iterator it; - union date32 date; - const char *text; - assert(j); - it = journal_iterator(j); - while(journal_next(&it, &date, &text)) if(!scan(&f, date, text)) goto catch; - goto finally; -catch: - flights_(&f); -finally: - return f; -} -const char *flights_to_string(const struct flight_tree *const f) - { return flight_tree_to_string(f); } -struct flight_tree_iterator flights_iterator(struct flight_tree *const f) - { return flight_tree_iterator(f); } -int flights_next(struct flight_tree_iterator *const it, union line64 *const k, - const struct flight **const v) { - assert(it && k && v); - if(!flight_tree_next(it)) return 0; - *k = flight_tree_key(it); - *v = flight_tree_value(it); - return 1; -} diff --git a/src/kjv.h b/src/kjv.h index 4a5bbdc..88c90bb 100644 --- a/src/kjv.h +++ b/src/kjv.h @@ -1,18 +1,109 @@ -#include "kjvcite.h" +#define BOOKS \ + X(Genesis),\ + X(Exodus),\ + X(Leviticus),\ + X(Numbers),\ + X(Deuteronomy),\ + X(Joshua),\ + X(Judges),\ + X(Ruth),\ + X(ISamuel),\ + X(IISamuel),\ + X(IKings),\ + X(IIKings),\ + X(IChronicles),\ + X(IIChronicles),\ + X(Ezra),\ + X(Nehemiah),\ + X(Esther),\ + X(Job),\ + X(Psalms),\ + X(Proverbs),\ + X(Ecclesiastes),\ + X(Song_of_Solomon),\ + X(Isaiah),\ + X(Jeremiah),\ + X(Lamentations),\ + X(Ezekiel),\ + X(Daniel),\ + X(Hosea),\ + X(Joel),\ + X(Amos),\ + X(Obadiah),\ + X(Jonah),\ + X(Micah),\ + X(Nahum),\ + X(Habakkuk),\ + X(Zephaniah),\ + X(Haggai),\ + X(Zechariah),\ + X(Malachi),\ + \ + X(Matthew),\ + X(Mark),\ + X(Luke),\ + X(John),\ + X(Acts),\ + X(Romans),\ + X(ICorinthians),\ + X(IICorinthians),\ + X(Galatians),\ + X(Ephesians),\ + X(Philippians),\ + X(Colossians),\ + X(IThessalonians),\ + X(IIThessalonians),\ + X(ITimothy),\ + X(IITimothy),\ + X(Titus),\ + X(Philemon),\ + X(Hebrews),\ + X(James),\ + X(IPeter),\ + X(IIPeter),\ + X(IJohn),\ + X(IIJohn),\ + X(IIIJohn),\ + X(Jude),\ + X(Revelation),\ + X(KJV_BOOK_SIZE) +#define X(book) book +enum kjv_book { BOOKS }; +#undef X +#define X(book) #book +static const char *kjv_book_string[] = { BOOKS }; +#undef X +#undef BOOKS +#include /* C99 */ +union kjvcite { + struct { uint32_t verse : 12, chapter : 13, book : 7; }; /* C11, reverse */ + uint32_t u32; +}; +struct kjvrange { union kjvcite start; uint32_t verse_end; }; +void kjvcite_to_string(const union kjvcite, char (*)[12]); -#define TREE_NAME kjvline -#define TREE_KEY union line64 -#define TREE_VALUE struct kjvrange -#define TREE_HEAD -#include "../src/tree.h" +#define TABLE_NAME kjvset +#define TABLE_KEY union kjvcite +#define TABLE_UINT uint32_t +#define TABLE_UNHASH +#define TABLE_HEAD +#include "../src/table.h" -#include /* fixme?? */ +#define TABLE_NAME kjvcount +#define TABLE_KEY union kjvcite +#define TABLE_UINT uint32_t +#define TABLE_VALUE unsigned +#define TABLE_UNHASH +#define TABLE_HEAD +#include "../src/table.h" -void kjv_line_(struct kjvline_tree *); -struct kjvline_tree kjv_line(struct journal *); -int kjv_line_is_empty(const struct kjvline_tree *); -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 **); -int kjv_graphs(struct journal *, struct sources *); +#include /* size_t */ +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 *); + +struct kjvset_table kjv_set(void); +void kjv_set_(struct kjvset_table *); +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/kjv.re.c b/src/kjv.re.c index 116e859..657e0ec 100644 --- a/src/kjv.re.c +++ b/src/kjv.re.c @@ -1,375 +1,231 @@ -/** @license 2022 Neil Edelman, distributed under the terms of the - [MIT License](https://opensource.org/licenses/MIT). +/** Run with a `kjv` sub-directory. Two functionaries: counts all words from + verses + dynamically and puts them into an unchanging `kjvcount_table` on initialisation. + Then has a set to each of the verses, `kjvset`, which starts off empty. + @license 2022 Neil Edelman, distributed under the terms of the + [MIT License](https://opensource.org/licenses/MIT). Uses the KJV at + [bible databases](https://github.com/scrollmapper/bible_databases/tree/master), + "All included Bible translations are in the public domain." + @std C11 */ - Scan journal entries for kjv references. */ - -#include "../src/source.h" #include "../src/kjv.h" #include "../src/pair.h" -#include /* C99 */ -#include +#include #include -#include +#include #include -#include +#include /* opendir readdir closedir */ +#include /* chdir (POSIX) (because I'm lazy) */ -static void kjvline_to_string(const union line64 line, const struct kjvrange *u, - char (*const a)[12]) { (void)u; date32_to_string(line.date, a); } -static int kjvline_compare(const union line64 a, const union line64 b) - { return a.u64 > b.u64; } -#define TREE_NAME kjvline -#define TREE_KEY union line64 -#define TREE_VALUE struct kjvrange -#define TREE_COMPARE -#define TREE_TO_STRING -#define TREE_BODY -#include "../src/tree.h" +void kjvcite_to_string(const union kjvcite x, char (*const a)[12]) + { sprintf(*a, "%.4s%" PRIu32 ":%" PRIu32, + kjv_book_string[x.book < KJV_BOOK_SIZE ? x.book : KJV_BOOK_SIZE], + x.chapter % 1000, x.verse % 1000); } + +/* Reversible hash map. */ +/** + on `x`. */ +static uint32_t lowbias32(uint32_t x) { + x ^= x >> 16; + x *= 0x7feb352dU; + x ^= x >> 15; + x *= 0x846ca68bU; + x ^= x >> 16; + return x; +} +/* Inverts `x`. */ +static uint32_t lowbias32_r(uint32_t x) { + x ^= x >> 16; + x *= 0x43021123U; + x ^= x >> 15 ^ x >> 30; + x *= 0x1d69e2a5U; + x ^= x >> 16; + return x; +} + +/* Set of verses. */ +static uint32_t kjvset_hash(const union kjvcite x) { return lowbias32(x.u32); } +static union kjvcite kjvset_unhash(const uint32_t x) + { union kjvcite k; k.u32 = lowbias32_r(x); return k; } +static void kjvset_to_string(const union kjvcite x, char (*const a)[12]) + { kjvcite_to_string(x, a); } +#define TABLE_NAME kjvset +#define TABLE_KEY union kjvcite +#define TABLE_UINT uint32_t +#define TABLE_UNHASH +#define TABLE_TO_STRING +#define TABLE_BODY +#include "../src/table.h" + +/* Derived information on verse word 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. */ +#define TABLE_UNHASH +#define TABLE_DEFAULT 0 +#define TABLE_TO_STRING +#define TABLE_BODY +#include "../src/table.h" -/*!conditions:re2c*/ - -static int scan(union date32 date, const char *const buffer, - struct kjvline_tree *const lines) { - const char *YYCURSOR, *YYMARKER, *yyt1, *yyt2, *yyt3, *s0, *s1, *t0, *t1; - enum kjv_book book = Revelation; - uint32_t chapter = 0, verse = 0, verse_end = 0; - enum YYCONDTYPE condition = yycline; - size_t line = 1; - const char *why = "unexpected"; - assert(buffer && lines); - YYCURSOR = YYMARKER = yyt1 = buffer; +/* Parse filename of books. */ +/*!re2c /**/ +re2c:yyfill:enable = 0; +re2c:define:YYCTYPE = char; +natural = [1-9][0-9]*; +whitespace = [ \t\v\f]; +word = [^ \t\v\f\n\x00]+; +*/ +/** `fn` contains "[*].txt", sticks that in `book_no`, otherwise + returns false. */ +static int looks_like_book_fn(const char *fn, unsigned *const book_no) { + const char *YYCURSOR = fn, *YYMARKER, *yyt1, *yyt2, *s0, *s1; + assert(fn && book_no); /*!re2c /**/ - re2c:define:YYCTYPE = char; - re2c:yyfill:enable = 0; + * + { return 0; } + @s0 natural @s1 [^.\x00]* ".txt" "\x00" + { return pair_to_natural(s0, s1, book_no); } + */ +} + + +/* This is the contents of the . */ +struct lex { + size_t line; + const char *cursor; + int error; + uint32_t chapter, verse, words; +}; +static struct lex lex(const char *cursor) { + struct lex lex; + assert(cursor); + lex.line = 1; + lex.cursor = cursor; + lex.error = 0; + lex.chapter = lex.verse = lex.words = 0; + return lex; +} +/*!conditions:re2c*/ +static int lex_next_verse(struct lex *const lex) { + const char *YYMARKER, *yyt1 = 0, *yyt2 = 0, *s0, *s1, *t0, *t1; + enum YYCONDTYPE condition = yycline; + /*!re2c /**/ + re2c:define:YYCURSOR = lex->cursor; re2c:define:YYGETCONDITION = "condition"; re2c:define:YYSETCONDITION = "condition = @@;"; re2c:define:YYGETCONDITION:naked = 1; - re2c:define:YYSETCONDITION:naked = 1; - - unix_control = [\x01-\x08\x0b-\x1f\x7f]; - ws = [ \t]; - glyph = [^] \ ("\x00" | "\n" | unix_control | ws); - natural = [1-9][0-9]*; - engage = ws+ "--" ws+; - /* (natural ":")? Don't use for memorizing and use for reading, I think? */ - /*("``"|"\"") This is not in the next book. */ - lookat = ws* natural ":" natural [ab]? ("-" natural [ab]?)? engage; - first = ("I" | "1") " "?; - second = ("II" | "2") " "?; - third = ("III" | "3") " "?; + re2c:define:YYSETCONDITION:naked = 1; */ + assert(lex && lex->cursor); + lex->error = 0; +scan: + /*!re2c /**/ + <*> * { return errno = EILSEQ, lex->error = 1, 0; } + [^[\]\n\x00]* "\n" { lex->line++; goto scan; } + "\x00" { return 0; } + "[" @s0 natural @s1 ":" @t0 natural @t1 "]" => verse { + if(!pair_to_natural(s0, s1, &lex->chapter) + || !pair_to_natural(t0, t1, &lex->verse)) + return errno = EILSEQ, lex->error = 1, 0; + lex->words = 0; + /*printf("%u:%u", lex->chapter, lex->verse);*/ + goto scan; + } + whitespace+ { goto scan; } + @s0 word @s1 { lex->words++; goto scan; } + "\n" { /*printf(" -> %u\n", lex->words);*/ lex->line++; return 1; } */ - for( ; ; ) { /*!re2c /**/ - /* Default ignore. */ - [^\n\x00] { continue; } - "\x00" { why = "no newline at end of file"; goto catch; } - "\x00" { return 1; } - "\n" => line { line++; continue; } - * :=> skip +} - /* Books. */ - "Genesis" / lookat => book { book = Genesis; continue; } - "Exodus" / lookat => book { book = Exodus; continue; } - "Leviticus" / lookat => book { book = Leviticus; continue; } - "Numbers" / lookat => book { book = Numbers; continue; } - "Deuteronomy" / lookat => book { book = Deuteronomy; continue; } - "Joshua" / lookat => book { book = Joshua; continue; } - "Judges" / lookat => book { book = Judges; continue; } - "Ruth" / lookat => book { book = Ruth; continue; } - first "Samuel" / lookat => book { book = ISamuel; continue; } - second "Samuel" / lookat => book { book = IISamuel; continue; } - first "Kings" / lookat => book { book = IKings; continue; } - second "Kings" / lookat => book { book = IIKings; continue; } - first "Chronicles" / lookat - => book { book = IChronicles; continue; } - second "Chronicles" / lookat - => book { book = IIChronicles; continue; } - "Ezra" / lookat => book { book = Ezra; continue; } - "Nehemiah" / lookat => book { book = Nehemiah; continue; } - "Esther" / lookat => book { book = Esther; continue; } - "Job" / lookat => book { book = Job; continue; } - "Psalms" / lookat => book { book = Psalms; continue; } - "Proverbs" / lookat => book { book = Proverbs; continue; } - "Ecclesiastes" / lookat - => book { book = Ecclesiastes; continue; } - "Song of Solomon" / lookat - => book { book = Song_of_Solomon; continue; } - "Isaiah" / lookat => book { book = Isaiah; continue; } - "Jeremiah" / lookat => book { book = Jeremiah; continue; } - "Lamentations" / lookat - => book { book = Lamentations; continue; } - "Ezekiel" / lookat => book { book = Ezekiel; continue; } - "Daniel" / lookat => book { book = Daniel; continue; } - "Hosea" / lookat => book { book = Hosea; continue; } - "Joel" / lookat => book { book = Joel; continue; } - "Amos" / lookat => book { book = Amos; continue; } - "Obadiah" / lookat => book { book = Obadiah; continue; } - "Jonah" / lookat => book { book = Jonah; continue; } - "Micah" / lookat => book { book = Micah; continue; } - "Nahum" / lookat => book { book = Nahum; continue; } - "Habakkuk" / lookat => book { book = Habakkuk; continue; } - "Zephaniah" / lookat => book { book = Zephaniah; continue; } - "Haggai" / lookat => book { book = Haggai; continue; } - "Zechariah" / lookat => book { book = Zechariah; continue; } - "Malachi" / lookat => book { book = Malachi; continue; } - "Matthew" / lookat => book { book = Matthew; continue; } - "Mark" / lookat => book { book = Mark; continue; } - "Luke" / lookat => book { book = Luke; continue; } - "John" / lookat => book { book = John; continue; } - "Acts" / lookat => book { book = Acts; continue; } - "Romans" / lookat => book { book = Romans; continue; } - first "Corinthians" / lookat - => book { book = ICorinthians; continue; } - second "Corinthians" / lookat - => book { book = IICorinthians; continue; } - "Galatians" / lookat => book { book = Galatians; continue; } - "Ephesians" / lookat => book { book = Ephesians; continue; } - "Philippians" / lookat => book { book = Philippians; continue; } - "Colossians" / lookat => book { book = Colossians; continue; } - first "Thessalonians" / lookat - => book { book = IThessalonians; continue; } - second "Thessalonians" / lookat - => book { book = IIThessalonians; continue; } - first "Timothy" / lookat => book { book = ITimothy; continue; } - second "Timothy" / lookat => book { book = IITimothy; continue; } - "Titus" / lookat => book { book = Titus; continue; } - "Philemon" / lookat => book { book = Philemon; continue; } - "Hebrews" / lookat => book { book = Hebrews; continue; } - "James" / lookat => book { book = James; continue; } - first "Peter" / lookat => book { book = IPeter; continue; } - second "Peter" / lookat => book { book = IIPeter; continue; } - first "John" / lookat => book { book = IJohn; continue; } - second "John" / lookat => book { book = IIJohn; continue; } - third "John" / lookat => book { book = IIIJohn; continue; } - "Jude" / lookat => book { book = Jude; continue; } - "Revelation" / lookat => book { book = Revelation; continue; } +/* 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_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; - /* Extract further information. */ - * { why = "default unrecognized"; goto catch; } - /* 19:15a, just ignore the a. */ - ws+ @s0 natural @s1 ":" @t0 natural @t1 [ab]? { - if(chapter || verse || verse_end) - { why = "reference unrecognized"; goto catch; } - if(!pair_to_natural(s0, s1, &chapter) - || !pair_to_natural(t0, t1, &verse)) - { why = "reference numerical error"; goto catch; } - continue; - } - "-" @s0 natural @s1 [ab]? { /* Verse range. */ - if(!chapter || !verse || verse_end) - { why = "range unrecognized"; goto catch; } - if(!pair_to_natural(s0, s1, &verse_end)) - { why = "range numerical error"; goto catch; } - continue; - } - engage => skip { - if(!chapter || !verse) { why = "missing information"; goto catch; } - if(verse_end && verse_end <= verse) - { why = "interval error"; goto catch; } /* 0 or valid. */ - const union line64 key - = {{ (uint32_t)line, {{ date.day, date.month, date.year }} }}; - struct kjvrange *value; - switch(kjvline_tree_assign(lines, key, &value)) { /* fixme */ - case TREE_PRESENT: why = "duplicate key"; - case TREE_ERROR: goto catch; - case TREE_ABSENT: - value->start.book = book; - value->start.chapter = chapter; - value->start.verse = verse; - value->verse_end = verse_end; - break; - } - book = Revelation, chapter = 0, verse = 0, verse_end = 0; - continue; - } - */ } - assert(0); /* Never gets here. */ -catch: - if(!errno) errno = EILSEQ; - { - char datestr[12]; - date32_to_string(date, &datestr); - fprintf(stderr, "KJV lines: %s.\n" - "%s line %zu: %s.\n", - kjvline_tree_to_string(lines), datestr, line, why); + 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; + while((de = readdir(dir))) { + unsigned ordinal; + char *unstable_backing; + if(!looks_like_book_fn(de->d_name, &ordinal)) continue; + /*fprintf(stderr, "<%s> ordinal: %u\n", de->d_name, ordinal);*/ + if(ordinal < 1 || ordinal > KJV_BOOK_SIZE) + { errno = ERANGE; goto catch; } /* Not in range. */ + if(build[b = ordinal - 1].is) /* Convert to zero-based. */ + { errno = EDOM; goto catch; } /* Is duplicate. */ + if(!(unstable_backing = text_append_file(&backing, de->d_name))) + goto catch; + build[b].is = 1; + build[b].offset = (size_t)(unstable_backing - backing.data); } - return 0; -} + if(closedir(dir) == -1) { dir = 0; goto catch; } dir = 0; -void kjv_line_(struct kjvline_tree *const f) { kjvline_tree_(f); } - -struct kjvline_tree kjv_line(struct journal *const j) { - struct kjvline_tree lines; - struct journal_iterator it; - union date32 date; - const char *text; - assert(j); - lines = kjvline_tree(); - it = journal_iterator(j); - while(journal_next(&it, &date, &text)) - if(!scan(date, text, &lines)) goto catch; - goto finally; -catch: - kjv_line_(&lines); -finally: - return lines; -} - -int kjv_line_is_empty(const struct kjvline_tree *const lines) - { return !lines || !lines->root.node; } - -const char *kjv_line_to_string(const struct kjvline_tree *const lines) - { return kjvline_tree_to_string(lines); } - -struct kjvline_tree_iterator kjv_line_iterator(struct kjvline_tree *const lines) - { return kjvline_tree_iterator(lines); } - -int kjv_line_next(struct kjvline_tree_iterator *const it, union line64 *const k, - const struct kjvrange **const v) { - assert(it && k && v); - if(!kjvline_tree_next(it)) return 0; - *k = kjvline_tree_key(it); - *v = kjvline_tree_value(it); - return 1; -} - -/** @return Success. */ -int kjv_graphs(struct journal *const jrnl, struct sources *const srcs) { - struct kjvline_tree lines = {0}; - struct kjvcount_table count = {0}; - struct kjvset_table set = kjv_set(); - size_t no_total; - const char *reason = 0; - - 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; } - - lines = kjv_line(jrnl); - fprintf(stderr, "KJV lines: %s.\n", kjv_line_to_string(&lines)); - if(kjv_line_is_empty(&lines)) - { reason = "KJV lines parsing failed"; goto catch; } - - struct kjvline_tree_iterator it = kjv_line_iterator(&lines); - union line64 line; - const struct kjvrange *range; - - /*printf("set term postscript eps enhanced\n" - "set output \"kjv.eps\"\n");*/ - /*printf("set terminal cairolatex standalone pdf size " - "16cm,10.5cm dashed transparent\n" - "set output \"kjv.tex\"\n");*/ - /* https://stackoverflow.com/a/12601553 */ - printf("set terminal pngcairo dashed transparent truecolor" - " size 840, 480 fontscale 1\n" - "set output \"kjv.png\"\n"); - printf("$Data <name.a) { errno = EDOM; goto catch; } - date32_to_string(line.date, &datestr); /* Date. */ - kjvcite_to_string(range->start, &citestr); /* KJV cite. */ - for(union kjvcite c = range->start; ; c.verse++) { - size_t w = kjv_count_get(&count, c); - words += w; - switch (kjv_set_add(&set, c)) { + /* Now backing is stable; count all the words for each verse. */ + for(b = 0; b < KJV_BOOK_SIZE; b++) { + struct lex x; + if(!build[b].is) { fprintf(stderr, "Missing book [%u]%s.\n", + b + 1, kjv_book_string[b]); errno = EDOM; goto catch; } + x = lex(backing.data + build[b].offset); + while(lex_next_verse(&x)) { + const union kjvcite cite + = { .book = b, .chapter = x.chapter, .verse = x.verse }; + unsigned *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: newwords += w; - case TABLE_PRESENT: break; - } /* while(); */ - if(!range->verse_end || range->verse_end <= c.verse) break; + case TABLE_ABSENT: break; + } + *words = x.words, *total += x.words; } - printf("%s, %s", datestr, citestr); - if(range->verse_end) printf("-%" PRIu32, range->verse_end); - printf(", %zu, %zu, %.*s\n", - words, newwords, (int)(src->name.b - src->name.a), src->name.a); + if(x.error) { fprintf(stderr, "[%u]%s on line %zu\n", + b + 1, kjv_book_string[b], x.line); goto catch; } } - printf("EOD\n" - "# theozh https://stackoverflow.com/a/75466214/2472827\n" - "# get a unique list from datablock\n" - "addToList(list,col) = list.( strstrt(list,'\"'.strcol(col).'\"') \\\n" - " > 0 ? '' : ' \"'.strcol(col).'\"')\n" - "Uniqs = ''\n" - "stats $Data u (Uniqs=addToList(Uniqs,5)) nooutput\n" - "Uniq(i) = word(Uniqs,i)\n" - "getIndex(s) = sum [_i=1:words(Uniqs)] s eq word(Uniqs,_i) ? _i : 0\n" - "\n" - "stats $Data u 3 nooutput\n" - "cumsum = STATS_sum\n" - "stats $Data u 4 nooutput\n" - "setsum = STATS_sum\n" - "\n" - "myTimeFmt = \"%%Y-%%m-%%d\"\n" - "set format x myTimeFmt timedate\n" - "set xtics format myTimeFmt rotate by -30\n" - "set format y \"%%g%%%%\"\n" - "set grid\n" - "set key out reverse Left noautotitle\n" - "set style fill solid 0.5\n" - "unset border\n" - "set autoscale xfix # max? hack: can't get x to extend further\n" - "\n" - "set label sprintf(\"%%u cumulative words (duplicate verses counted)\"," - " cumsum) center at graph 0.5, first cumsum*100/%zu offset 0,0.5\n" - "set label sprintf(\"%%u unique KJV verse words memorized\", setsum) " - "center at graph 0.5, first setsum*100/%zu offset 0,0.5\n" - "\n" - "plot \\\n" - " cumsum*100/%zu w l lc \"grey\" dt 2 lw 1, \\\n" - " total=0 $Data u" - " (timecolumn(1,myTimeFmt)):(dy=$3*100/%zu,total=total+dy) \\\n" - " w steps lc \"grey\" dt 2 lw 1, \\\n" /* `pngcairo` has trouble :[. */ - " total=0 $Data u" - " (timecolumn(1,myTimeFmt)):(dy=$4*100/%zu,total=total+dy) \\\n" - " w steps lc \"black\" dt 1 lw 1, \\\n" - " setsum*100/%zu w l lc \"black\" dt 1 lw 1, \\\n" - " total=0 '' u \\\n" - " (timecolumn(1,myTimeFmt)): \\\n" - " (dy=$4*100/%zu,total=total+dy,total/2.): \\\n" - " (43200): \\\n" - " (total/2.): \\\n" - " (getIndex(strcol(5))) w boxxy lc var lw 1, \\\n" - " for [i=1:words(Uniqs)] keyentry w boxxy lc i ti Uniq(i)\n", - no_total, no_total, no_total, no_total, no_total, no_total, no_total); goto finally; catch: - perror(reason); -finally: - kjv_line_(&lines); + *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: kjv_count_(&count); - return !reason; -} - -int main(void) { - int success = EXIT_SUCCESS; - const char *reason = 0; - errno = 0; - struct journal jrnl = {0}; - struct sources srcs = {0}; - - jrnl = journal("../journal"); - fprintf(stderr, "Journal: %s.\n", journal_to_string(&jrnl)); - if(journal_is_empty(&jrnl)) - { reason = "journal failed to load"; goto catch; } - - srcs = sources(&jrnl); - //fprintf(stderr, "Sources: %s.\n", sources_to_string(&srcs)); - if(sources_is_empty(&srcs)) - { reason = "sources failed to parse"; goto catch; } - - if(!kjv_graphs(&jrnl, &srcs)) - { reason = "graphs failed"; goto catch; } - - goto finally; -catch: - success = EXIT_FAILURE; - perror("journal"); - if(reason) fprintf(stderr, "Details: %s.\n", reason); finally: - sources_(&srcs); - journal_(&jrnl); - return success; + 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); } + +/* 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; } diff --git a/src/kjvcite.h b/src/kjvcite.h deleted file mode 100644 index 88c90bb..0000000 --- a/src/kjvcite.h +++ /dev/null @@ -1,109 +0,0 @@ -#define BOOKS \ - X(Genesis),\ - X(Exodus),\ - X(Leviticus),\ - X(Numbers),\ - X(Deuteronomy),\ - X(Joshua),\ - X(Judges),\ - X(Ruth),\ - X(ISamuel),\ - X(IISamuel),\ - X(IKings),\ - X(IIKings),\ - X(IChronicles),\ - X(IIChronicles),\ - X(Ezra),\ - X(Nehemiah),\ - X(Esther),\ - X(Job),\ - X(Psalms),\ - X(Proverbs),\ - X(Ecclesiastes),\ - X(Song_of_Solomon),\ - X(Isaiah),\ - X(Jeremiah),\ - X(Lamentations),\ - X(Ezekiel),\ - X(Daniel),\ - X(Hosea),\ - X(Joel),\ - X(Amos),\ - X(Obadiah),\ - X(Jonah),\ - X(Micah),\ - X(Nahum),\ - X(Habakkuk),\ - X(Zephaniah),\ - X(Haggai),\ - X(Zechariah),\ - X(Malachi),\ - \ - X(Matthew),\ - X(Mark),\ - X(Luke),\ - X(John),\ - X(Acts),\ - X(Romans),\ - X(ICorinthians),\ - X(IICorinthians),\ - X(Galatians),\ - X(Ephesians),\ - X(Philippians),\ - X(Colossians),\ - X(IThessalonians),\ - X(IIThessalonians),\ - X(ITimothy),\ - X(IITimothy),\ - X(Titus),\ - X(Philemon),\ - X(Hebrews),\ - X(James),\ - X(IPeter),\ - X(IIPeter),\ - X(IJohn),\ - X(IIJohn),\ - X(IIIJohn),\ - X(Jude),\ - X(Revelation),\ - X(KJV_BOOK_SIZE) -#define X(book) book -enum kjv_book { BOOKS }; -#undef X -#define X(book) #book -static const char *kjv_book_string[] = { BOOKS }; -#undef X -#undef BOOKS -#include /* C99 */ -union kjvcite { - struct { uint32_t verse : 12, chapter : 13, book : 7; }; /* C11, reverse */ - uint32_t u32; -}; -struct kjvrange { union kjvcite start; uint32_t verse_end; }; -void kjvcite_to_string(const union kjvcite, char (*)[12]); - -#define TABLE_NAME kjvset -#define TABLE_KEY union kjvcite -#define TABLE_UINT uint32_t -#define TABLE_UNHASH -#define TABLE_HEAD -#include "../src/table.h" - -#define TABLE_NAME kjvcount -#define TABLE_KEY union kjvcite -#define TABLE_UINT uint32_t -#define TABLE_VALUE unsigned -#define TABLE_UNHASH -#define TABLE_HEAD -#include "../src/table.h" - -#include /* size_t */ -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 *); - -struct kjvset_table kjv_set(void); -void kjv_set_(struct kjvset_table *); -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/kjvcite.re.c b/src/kjvcite.re.c deleted file mode 100644 index 184a497..0000000 --- a/src/kjvcite.re.c +++ /dev/null @@ -1,231 +0,0 @@ -/** Run with a `kjv` sub-directory. Two functionaries: counts all words from - verses - dynamically and puts them into an unchanging `kjvcount_table` on initialisation. - Then has a set to each of the verses, `kjvset`, which starts off empty. - @license 2022 Neil Edelman, distributed under the terms of the - [MIT License](https://opensource.org/licenses/MIT). Uses the KJV at - [bible databases](https://github.com/scrollmapper/bible_databases/tree/master), - "All included Bible translations are in the public domain." - @std C11 */ - -#include "../src/kjvcite.h" -#include "../src/pair.h" -#include -#include -#include -#include -#include /* opendir readdir closedir */ -#include /* chdir (POSIX) (because I'm lazy) */ - - -void kjvcite_to_string(const union kjvcite x, char (*const a)[12]) - { sprintf(*a, "%.4s%" PRIu32 ":%" PRIu32, - kjv_book_string[x.book < KJV_BOOK_SIZE ? x.book : KJV_BOOK_SIZE], - x.chapter % 1000, x.verse % 1000); } - -/* Reversible hash map. */ -/** - on `x`. */ -static uint32_t lowbias32(uint32_t x) { - x ^= x >> 16; - x *= 0x7feb352dU; - x ^= x >> 15; - x *= 0x846ca68bU; - x ^= x >> 16; - return x; -} -/* Inverts `x`. */ -static uint32_t lowbias32_r(uint32_t x) { - x ^= x >> 16; - x *= 0x43021123U; - x ^= x >> 15 ^ x >> 30; - x *= 0x1d69e2a5U; - x ^= x >> 16; - return x; -} - -/* Set of verses. */ -static uint32_t kjvset_hash(const union kjvcite x) { return lowbias32(x.u32); } -static union kjvcite kjvset_unhash(const uint32_t x) - { union kjvcite k; k.u32 = lowbias32_r(x); return k; } -static void kjvset_to_string(const union kjvcite x, char (*const a)[12]) - { kjvcite_to_string(x, a); } -#define TABLE_NAME kjvset -#define TABLE_KEY union kjvcite -#define TABLE_UINT uint32_t -#define TABLE_UNHASH -#define TABLE_TO_STRING -#define TABLE_BODY -#include "../src/table.h" - -/* Derived information on verse word 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. */ -#define TABLE_UNHASH -#define TABLE_DEFAULT 0 -#define TABLE_TO_STRING -#define TABLE_BODY -#include "../src/table.h" - - -/* Parse filename of books. */ -/*!re2c /**/ -re2c:yyfill:enable = 0; -re2c:define:YYCTYPE = char; -natural = [1-9][0-9]*; -whitespace = [ \t\v\f]; -word = [^ \t\v\f\n\x00]+; -*/ -/** `fn` contains "[*].txt", sticks that in `book_no`, otherwise - returns false. */ -static int looks_like_book_fn(const char *fn, unsigned *const book_no) { - const char *YYCURSOR = fn, *YYMARKER, *yyt1, *yyt2, *s0, *s1; - assert(fn && book_no); - /*!re2c /**/ - * - { return 0; } - @s0 natural @s1 [^.\x00]* ".txt" "\x00" - { return pair_to_natural(s0, s1, book_no); } - */ -} - - -/* This is the contents of the . */ -struct lex { - size_t line; - const char *cursor; - int error; - uint32_t chapter, verse, words; -}; -static struct lex lex(const char *cursor) { - struct lex lex; - assert(cursor); - lex.line = 1; - lex.cursor = cursor; - lex.error = 0; - lex.chapter = lex.verse = lex.words = 0; - return lex; -} -/*!conditions:re2c*/ -static int lex_next_verse(struct lex *const lex) { - const char *YYMARKER, *yyt1 = 0, *yyt2 = 0, *s0, *s1, *t0, *t1; - enum YYCONDTYPE condition = yycline; - /*!re2c /**/ - re2c:define:YYCURSOR = lex->cursor; - re2c:define:YYGETCONDITION = "condition"; - re2c:define:YYSETCONDITION = "condition = @@;"; - re2c:define:YYGETCONDITION:naked = 1; - re2c:define:YYSETCONDITION:naked = 1; */ - assert(lex && lex->cursor); - lex->error = 0; -scan: - /*!re2c /**/ - <*> * { return errno = EILSEQ, lex->error = 1, 0; } - [^[\]\n\x00]* "\n" { lex->line++; goto scan; } - "\x00" { return 0; } - "[" @s0 natural @s1 ":" @t0 natural @t1 "]" => verse { - if(!pair_to_natural(s0, s1, &lex->chapter) - || !pair_to_natural(t0, t1, &lex->verse)) - return errno = EILSEQ, lex->error = 1, 0; - lex->words = 0; - /*printf("%u:%u", lex->chapter, lex->verse);*/ - goto scan; - } - whitespace+ { goto scan; } - @s0 word @s1 { lex->words++; goto scan; } - "\n" { /*printf(" -> %u\n", lex->words);*/ lex->line++; return 1; } - */ -} - -/* 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_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; - while((de = readdir(dir))) { - unsigned ordinal; - char *unstable_backing; - if(!looks_like_book_fn(de->d_name, &ordinal)) continue; - /*fprintf(stderr, "<%s> ordinal: %u\n", de->d_name, ordinal);*/ - if(ordinal < 1 || ordinal > KJV_BOOK_SIZE) - { errno = ERANGE; goto catch; } /* Not in range. */ - if(build[b = ordinal - 1].is) /* Convert to zero-based. */ - { errno = EDOM; goto catch; } /* Is duplicate. */ - if(!(unstable_backing = text_append_file(&backing, de->d_name))) - goto catch; - build[b].is = 1; - build[b].offset = (size_t)(unstable_backing - backing.data); - } - if(closedir(dir) == -1) { dir = 0; goto catch; } dir = 0; - - /* Now backing is stable; count all the words for each verse. */ - for(b = 0; b < KJV_BOOK_SIZE; b++) { - struct lex x; - if(!build[b].is) { fprintf(stderr, "Missing book [%u]%s.\n", - b + 1, kjv_book_string[b]); errno = EDOM; goto catch; } - x = lex(backing.data + build[b].offset); - while(lex_next_verse(&x)) { - const union kjvcite cite - = { .book = b, .chapter = x.chapter, .verse = x.verse }; - unsigned *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, *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: - 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); } - -/* 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; } diff --git a/src/scan.h b/src/scan.h index 639f81c..f2b64f9 100644 --- a/src/scan.h +++ b/src/scan.h @@ -64,7 +64,7 @@ struct flight { #include "../src/tree.h" -#include "kjvcite.h" +#include "kjv.h" #define TREE_NAME kjvline #define TREE_KEY union line64 #define TREE_VALUE struct kjvrange @@ -90,6 +90,7 @@ struct scan { void scan_(struct scan *); struct scan scan(struct journal *); +const struct source *scan_source_lookup(struct scan *, const union line64); void scan_score_graph(struct scan *); void scan_glider_graph(struct scan *); void scan_flight_graph(struct scan *); diff --git a/src/scan.re.c b/src/scan.re.c index 33fed24..115403d 100644 --- a/src/scan.re.c +++ b/src/scan.re.c @@ -580,8 +580,7 @@ finally: return scan; } -/** Lookup the last source in `range` in sources `s`. They are invalidated on - adding a source, (currently fine because we get all at once.) */ +/** Lookup the source of `x` in `scan`, if any. */ static const struct source *source_lookup(struct scan *const scan, const union line64 x) { struct linemap_tree_iterator it; @@ -594,6 +593,10 @@ static const struct source *source_lookup(struct scan *const scan, ? *linemap_tree_value(&it) : 0); } +/** Lookup the source of `x` in `scan`, if any; public function for testing. */ +const struct source *scan_source_lookup(struct scan *const scan, + const union line64 x) { return source_lookup(scan, x); } + void scan_score_graph(struct scan *const scan) { struct linemap_tree_iterator it = linemap_tree_iterator(&scan->scores.dates); diff --git a/src/scan_flight.h b/src/scan_flight.h deleted file mode 100644 index 8b17e5b..0000000 --- a/src/scan_flight.h +++ /dev/null @@ -1,4 +0,0 @@ -#include "helper.h" -#include "flight.h" - -int flight_scan(union date32, const char *, struct flight_tree *); diff --git a/src/scan_flight.re.c b/src/scan_flight.re.c deleted file mode 100644 index d1adf1b..0000000 --- a/src/scan_flight.re.c +++ /dev/null @@ -1,65 +0,0 @@ -/** @license 2022 Neil Edelman, distributed under the terms of the - [MIT License](https://opensource.org/licenses/MIT). - - Scan journal entries for kjv references. */ - -#include "../src/journal.h" -#include "../src/scan_flight.h" -#include /* C99 */ -#include -#include -#include -#include - -/*!conditions:re2c*/ - -int flight_scan(union date32 date, const char *const buffer, - struct flights *const f) { - const char *YYCURSOR, *YYMARKER, *yyt1, *yyt2, *yyt3, *s0, *s1, *t0, *t1; - enum flight_type type = Glider; - enum YYCONDTYPE condition = yycline; - size_t line = 1; - char datestr[12] = {0}; - const char *why = "unexpected"; - assert(buffer && f); - YYCURSOR = YYMARKER = yyt1 = buffer; - /*!re2c /**/ - re2c:define:YYCTYPE = char; - re2c:yyfill:enable = 0; - re2c:define:YYGETCONDITION = "condition"; - re2c:define:YYSETCONDITION = "condition = @@;"; - re2c:define:YYGETCONDITION:naked = 1; - re2c:define:YYSETCONDITION:naked = 1; - - unix_control = [\x01-\x08\x0b-\x1f\x7f]; - ws = [ \t]; - glyph = [^] \ ("\x00" | "\n" | unix_control | ws); - natural = [1-9][0-9]*; - */ - for( ; ; ) { /*!re2c /**/ - /* Default ignore. */ - [^\n\x00] { continue; } - "\x00" { why = "no newline at end of file"; goto catch; } - "\x00" { return 1; } - "\n" => line { line++; continue; } - * :=> skip - - /* Except these two. */ - "[glider]" :=> glider - "[flight]" :=> flight - - /* "M" - Motor Car Tow - "W" - Winch - "A" - Aero Tow */ - /* type; registration; launch -- landing; pic; sic; - single engine day dual; pilot; instrument simulated; actual; remarks */ - * { why = "default unrecognized"; goto catch; } - */ } - assert(0); /* Never gets here. */ -catch: - if(!errno) errno = EILSEQ; - date32_to_string(date, &datestr); - fprintf(stderr, "%s\n" - "%s line %zu: %s.\n", buffer, datestr, line, why); - return 0; -} diff --git a/src/scores.h b/src/scores.h deleted file mode 100644 index 24151ca..0000000 --- a/src/scores.h +++ /dev/null @@ -1,30 +0,0 @@ -#include "pair.h" /* pair */ -#include "journal.h" /* size_t, date32, line64 */ -struct score { - struct pair key, name; - union date32 date, last; - unsigned edges, tempscore; -}; - -#define ARRAY_NAME scorelist -#define ARRAY_TYPE struct score -#define ARRAY_HEAD -#include "../src/array.h" - -#define TREE_NAME score -#define TREE_KEY union line64 -#define TREE_VALUE size_t -#define TREE_HEAD -#include "../src/tree.h" - -struct scores { - struct scorelist_array list; - struct pairmap_table map; - struct score_tree dates; -}; - -void scores_(struct scores *); -struct scores scores(struct journal *); -int scores_is_empty(const struct scores *); -int scores_graph(struct scores *); -const char *scores_to_string(const struct scores *); diff --git a/src/scores.re.c b/src/scores.re.c deleted file mode 100644 index 08117b0..0000000 --- a/src/scores.re.c +++ /dev/null @@ -1,282 +0,0 @@ -/** @license 2022 Neil Edelman, distributed under the terms of the - [MIT License](https://opensource.org/licenses/MIT). - - Scan journal entries for score. */ - -#include "../src/journal.h" -#include "../src/scores.h" -#include -#include - - -/* One array. */ -static void scorelist_to_string(const struct score *const s, - char (*const z)[12]) { - const char *a = s->name.a, *b; - char *y = *z; - b = s->name.b <= a + 11 ? s->name.b : a + 11; - while(a < b) *(y++) = *(a++); - *y = '\0'; -} -#define ARRAY_NAME scorelist -#define ARRAY_TYPE struct score -#define ARRAY_TO_STRING -#define ARRAY_BODY -#include "../src/array.h" - - -/* Tree mapping from date-line to indices. */ -static void score_to_string(const union line64 line, const size_t *const u, - char (*const a)[12]) { (void)u; date32_to_string(line.date, a); } -static int score_compare(const union line64 a, const union line64 b) - { return a.u64 > b.u64; } -#define TREE_NAME score -#define TREE_KEY union line64 -#define TREE_VALUE size_t -#define TREE_COMPARE -#define TREE_TO_STRING -#define TREE_DEFAULT 0 -#define TREE_BODY -#include "../src/tree.h" - - -/*!conditions:re2c*/ - -static int scan(union date32 date, const char *const buffer, - struct scores *const scores) { - const char *YYCURSOR, *YYMARKER, *yyt1, *yyt2, *s0, *s1; - enum YYCONDTYPE condition = yycline; - size_t line = 1; - char datestr[12] = {0}; - const char *why = "unexpected"; - struct score *score = 0; - assert(buffer && scores); - YYCURSOR = YYMARKER = yyt1 = buffer; - /*!re2c /**/ - re2c:define:YYCTYPE = char; - re2c:yyfill:enable = 0; - re2c:define:YYGETCONDITION = "condition"; - re2c:define:YYSETCONDITION = "condition = @@;"; - re2c:define:YYGETCONDITION:naked = 1; - re2c:define:YYSETCONDITION:naked = 1; - - ws = [ \t]; - glyph = [^\x00-\x20\x7f]; // [^\x00\n\t ] + all weird - semitext = glyph \ ";"; - natural = [1-9][0-9]*; - uint = [0-9]+; - keyword = [A-Za-z0-9][A-Za-z0-9_-]*; - date = natural "-" [0-1][0-9] "-" [0-3][0-9]; - */ - for( ; ; ) { /*!re2c /**/ - /* Default ignore. */ - [^\n\x00] { continue; } - "\x00" { why = "no newline at end of file"; goto catch; } - "\x00" { return 1; } - "\n" => line { line++; continue; } - * :=> skip - "::" / [^:] :=> score - - * { why = "score unrecognized"; goto catch; } - /* Already there. Use the map to get the index from the keyword and - then stick a marker in the tree with that index. */ - @s0 keyword @s1 / "\n" => skip { also_add_to_tree: { - const struct pair keyword = pair(s0, s1); - const union line64 key = { { (uint32_t)line, date } }; - size_t idx, *pidx; - if(line > UINT32_MAX) - { errno = ERANGE; why = "too many lines of text"; goto catch; } - if(!(idx = pair_map_table_get(&scores->map, keyword))) - { why = "keyword not introduced"; goto catch; } - if(scores->list.data[idx].last.u32 >= date.u32) - { why = "duplicate key in same day"; goto catch; } - scores->list.data[idx].last.u32 = date.u32; - switch(score_tree_bulk_assign(&scores->dates, key, &pidx)) { - case TREE_PRESENT: assert(0); why = "duplicate key"; /* _Sic_. */ - case TREE_ERROR: goto catch; - case TREE_ABSENT: *pidx = idx; break; - } - /*date32_to_string(date, &datestr); - fprintf(stderr, "%s: <%.*s>\n", datestr, (int)(s1 - s0), s0);*/ - continue; - } } - /* New score. */ - @s0 keyword @s1 ":" => score_name { - size_t *idx; - switch(pair_map_table_assign(&scores->map, pair(s0, s1), &idx)) { - case TABLE_PRESENT: errno = EDOM; why = "new keyword already used"; - case TABLE_ERROR: goto catch; /* _Sic_. */ - case TABLE_ABSENT: *idx = 0; break; - } - if(!(score = scorelist_array_new(&scores->list))) goto catch; - *idx = (size_t)(score - scores->list.data); - /*struct pair key, name; union date32 date, last; unsigned edges;*/ - score->key.a = s0, score->key.b = s1; - score->name.a = 0, score->name.b = 0; - score->date.u32 = score->last.u32 = 0; - score->edges = 0, score->tempscore = 0; - date32_to_string(date, &datestr); - fprintf(stderr, "%s: new score <%.*s> stored in list at %zu.\n", - datestr, (int)(s1 - s0), s0, *idx); - goto also_add_to_tree; - } - * { why = "name unrecognized"; goto catch; } - * { why = "date unrecognized"; goto catch; } - * { why = "edges unrecognized"; goto catch; } - ws* @s0 semitext+ (" " semitext+)* @s1 /* ws* */ ";" - => score_date { - assert(score); - score->name.a = s0, score->name.b = s1; - } - ws* "~"? @s0 date ws* ";" => score_edges { - assert(score); - if(!pair_to_date(s0, &score->date)) goto catch; - } - ws* "~"? @s0 uint @s1 ws* / "\n" => skip { - assert(score); - if(!pair_to_natural(s0, s1, &score->edges)) goto catch; - score = 0; /* Done. */ - } - */ } - assert(0); /* Never gets here. */ -catch: - if(!errno) errno = EILSEQ; - date32_to_string(date, &datestr); - fprintf(stderr, "%s line %zu: %s.\n", datestr, line, why); - return 0; -} - -void scores_(struct scores *const s) { - if(!s) return; - score_tree_(&s->dates); - pair_map_table_(&s->map); - scorelist_array_(&s->list); -} - -struct scores scores(struct journal *const j) { - struct scores s - = { scorelist_array(), pair_map_table(), score_tree() }; - struct journal_iterator it; - union date32 k; - const char *v; - assert(j); - { /* Null is the first item for convenience, (TABLE_DEFAULT). */ - struct score *nul; - if(!(nul = scorelist_array_new(&s.list))) goto catch; - nul->key.a = nul->key.b = nul->name.a = nul->name.b = 0; - nul->date.u32 = 0; - nul->edges = 0; - } - it = journal_iterator(j); - while(journal_next(&it, &k, &v)) if(!scan(k, v, &s)) goto catch; - fprintf(stderr, "List of scores: %s.\n" - "Mapped to indices: %s.\n" - "Date-line tree: %s.\n", scorelist_array_to_string(&s.list), - pair_map_table_to_string(&s.map), score_tree_to_string(&s.dates)); - goto finally; -catch: - scores_(&s); -finally: - return s; -} - -int scores_is_empty(const struct scores *const s) - { return !s || !s->dates.root.node; } - -const char *scores_to_string(const struct scores *const s) - { return assert(s), scorelist_array_to_string(&s->list); } - -int scores_graph(struct scores *const scrs) { - struct score_tree_iterator it = score_tree_iterator(&scrs->dates); - union line64 line; - struct score *score; - - /* Set score to zero to verify count with paper journal. */ - for(size_t i = 0; i < scrs->list.size; i++) - scrs->list.data[i].tempscore = 0; - - /* 840 with legend; only useful to me. */ - printf("set terminal pngcairo dashed transparent truecolor" - " size 840, 480 fontscale 1\n" - "set output \"score.png\"\n"); - printf("$Data <list.data + *score_tree_value(&it); - char datestr[12]; - date32_to_string(line.date, &datestr); - score->tempscore++; - printf("%s, %.*s, %u\n", datestr, - (int)(score->key.b - score->key.a), score->key.a, score->tempscore); - } - printf("EOD\n" - "# theozh https://stackoverflow.com/a/75466214/2472827\n" - "# get a unique list from datablock\n" - "addToList(list,col) = list.( strstrt(list,'\"'.strcol(col).'\"') \\\n" - " > 0 ? '' : ' \"'.strcol(col).'\"')\n" - "Uniqs = ''\n" - "stats $Data u (Uniqs=addToList(Uniqs,2)) nooutput\n" - "Uniq(i) = word(Uniqs,i)\n" - "getIndex(s) = sum [_i=1:words(Uniqs)] s eq word(Uniqs,_i) ? _i : 0\n" - "\n" - /*"stats $Data u 3 nooutput\n" - "cumsum = STATS_sum\n" - "stats $Data u 4 nooutput\n" - "setsum = STATS_sum\n" - "\n"*/ - "myTimeFmt = \"%%Y-%%m-%%d\"\n" - "set format x myTimeFmt timedate\n" - "set xtics format myTimeFmt rotate by -30\n" - "set ylabel \"satisfaction and happiness CDF (days)\"\n" - "set grid\n" - "set key out reverse Left noautotitle\n" - "set style fill solid 0.5\n" - "unset border\n" - "unset key\n" - "set autoscale xfix # max? hack: can't get x to extend further\n" - "\n" - /*"set label sprintf(\"%%u cumulative words (duplicate verses counted)\"," - " cumsum) center at graph 0.5, first cumsum*100/%zu offset 0,0.5\n" - "set label sprintf(\"%%u unique KJV verse words memorized\", setsum) " - "center at graph 0.5, first setsum*100/%zu offset 0,0.5\n" - "\n"*/ - "plot \\\n" - " total=0 $Data u" - " (timecolumn(1,myTimeFmt)):(total=total+1) \\\n" - " w steps lc \"black\" dt 1 lw 1, \\\n" - " total=0 '' u \\\n" - " (timecolumn(1,myTimeFmt)): \\\n" - " (total=total+1,total/2.): \\\n" - " (43200): \\\n" - " (total/2.): \\\n" - " (getIndex(strcol(2))) w boxxy lc var lw 1, \\\n" - " for [i=1:words(Uniqs)] keyentry w boxxy lc i ti Uniq(i)\n"); - return 1; -} - -#include - -int main(void) { - const char *fail = 0; - struct journal jrnl = {0}; - struct scores scrs = {0}; - errno = 0; - - jrnl = journal("../journal"); - fprintf(stderr, "Journal: %s.\n", journal_to_string(&jrnl)); - if(journal_is_empty(&jrnl)) { fail = "journal failed to load"; goto catch; } - scrs = scores(&jrnl); - fprintf(stderr, "Scores: %s.\n", scores_to_string(&scrs)); - if(scores_is_empty(&scrs)) { fail = "scores failed to parse"; goto catch; } - if(!scores_graph(&scrs)) goto catch; - goto finally; - -catch: - if(!fail) fail = "score"; - perror(fail); -finally: - scores_(&scrs); - journal_(&jrnl); - return fail ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/source.h b/src/source.h deleted file mode 100644 index d6729f1..0000000 --- a/src/source.h +++ /dev/null @@ -1,28 +0,0 @@ -#include "pair.h" /* pair */ -struct source { struct pair name, desc; }; - -#define ARRAY_NAME sourcelist -#define ARRAY_TYPE struct source -#define ARRAY_HEAD -#include "../src/array.h" - -#include /* size_t */ -#include "../src/journal.h" /* line64 */ - -#define TREE_NAME source -#define TREE_KEY union line64 -#define TREE_VALUE size_t -#define TREE_HEAD -#include "../src/tree.h" - -struct sources { - struct sourcelist_array list; - struct pairmap_table map; - struct source_tree dates; -}; -struct source *sources_add(struct sources *, const union line64); -struct sources sources(struct journal *); -void sources_(struct sources *); -int sources_is_empty(const struct sources *); -const char *sources_to_string(const struct sources *); -const struct source *source_lookup(struct sources *, const union line64); diff --git a/src/source.re.c b/src/source.re.c deleted file mode 100644 index 673c49c..0000000 --- a/src/source.re.c +++ /dev/null @@ -1,170 +0,0 @@ -/** @license 2023 Neil Edelman, distributed under the terms of the - [MIT License](https://opensource.org/licenses/MIT). - @std C11 */ -#include "../src/source.h" -#include -#include -#include -#include - - -/* `sourcelist` is an array of all the sources. */ -static void sourcelist_to_string(const struct source *const s, - char (*const z)[12]) { - const char *a = s->name.a, *b; - char *y = *z; - b = s->name.b <= a + 11 ? s->name.b : a + 11; - while(a < b) *(y++) = *(a++); - *y = '\0'; -} -#define ARRAY_NAME sourcelist -#define ARRAY_TYPE struct source -#define ARRAY_TO_STRING -#define ARRAY_BODY -#include "../src/array.h" - - -/* `source` is a tree mapping from date-line to indices in `sourcelist`. */ -static void source_to_string(const union line64 line, const size_t *const u, - char (*const a)[12]) { (void)u; date32_to_string(line.date, a); } -static int source_compare(const union line64 a, const union line64 b) - { return a.u64 > b.u64; } -#define TREE_NAME source -#define TREE_KEY union line64 -#define TREE_VALUE size_t /* Index into source list. */ -#define TREE_COMPARE -#define TREE_TO_STRING -#define TREE_DEFAULT 0 -#define TREE_BODY -#include "../src/tree.h" - - -/*!conditions:re2c*/ - -static int scan(union date32 date, const char *const buffer, - struct sources *const s) { - const char *YYCURSOR, *YYMARKER, *yyt1, *yyt2, *s0, *s1; - enum YYCONDTYPE condition = yycline; - size_t line = 1; - char datestr[12] = {0}; - const char *why = "unexpected"; - assert(buffer && s); - YYCURSOR = YYMARKER = yyt1 = buffer; - /*!re2c /**/ - re2c:define:YYCTYPE = char; - re2c:yyfill:enable = 0; - re2c:define:YYGETCONDITION = "condition"; - re2c:define:YYSETCONDITION = "condition = @@;"; - re2c:define:YYGETCONDITION:naked = 1; - re2c:define:YYSETCONDITION:naked = 1; - - unix_control = [\x01-\x08\x0b-\x1f\x7f]; - ws = [ \t]; - glyph = [^] \ ("\x00" | "\n" | unix_control | ws); - keyword = [A-Za-z0-9][A-Za-z0-9_-]*; - */ - for( ; ; ) { - /*!re2c /**/ - /* Default ignore. */ - [^\n\x00] { continue; } - "\x00" { why = "no newline at end of file"; goto catch; } - "\x00" { return 1; } - "\n" => line { line++; continue; } - * :=> skip - "--" / [^-] :=> source - - * { why = "default source unrecognized"; goto catch; } - @s0 keyword @s1 / "\n" => skip { also_add_to_tree: { - const struct pair keyword = pair(s0, s1); - const union line64 key = { { (uint32_t)line, date } }; - size_t i, *pi; - if(line > UINT32_MAX) - { errno = ERANGE; why = "too many lines of text"; goto catch; } - if(!(i = pair_map_table_get(&s->map, keyword))) - { why = "keyword not introduced"; goto catch; } - /* fixme: bulk */ - switch(source_tree_assign(&s->dates, key, &pi)) { - case TREE_PRESENT: why = "duplicate key"; /* _Sic_. */ - case TREE_ERROR: goto catch; - case TREE_ABSENT: *pi = i; break; - } - /*date32_to_string(date, &datestr); - printf("%s: <%.*s>\n", datestr, (int)(s1 - s0), s0);*/ - continue; - } } - /* New source. This is lazy and will pickup trailing spaces. */ - @s0 keyword @s1 ":" [^\x00\n]+ / "\n" => skip { - struct pair keyword = pair(s0, s1); - size_t *idx; - struct source *source; - switch(pair_map_table_assign(&s->map, keyword, &idx)) { - case TABLE_PRESENT: errno = EDOM; why = "new keyword already used"; - case TABLE_ERROR: goto catch; /* /\ _Sic_. */ - case TABLE_ABSENT: *idx = 0; break; /* Good. */ - } - if(!(source = sourcelist_array_new(&s->list))) goto catch; - *idx = (size_t)(source - s->list.data); - source->name.a = s0, source->name.b = s1; - source->desc.a = 0, source->desc.b = 0; - date32_to_string(date, &datestr); - fprintf(stderr, "%s: new source <%.*s> stored in list at %zu.\n", - datestr, (int)(s1 - s0), s0, *idx); - goto also_add_to_tree; - } - */ } - assert(0); /* Never gets here. */ -catch: - if(!errno) errno = EILSEQ; - date32_to_string(date, &datestr); - fprintf(stderr, "%s line %zu: %s.\n", datestr, line, why); - return 0; -} - -void sources_(struct sources *const s) { - if(!s) return; - source_tree_(&s->dates); - pair_map_table_(&s->map); - sourcelist_array_(&s->list); -} - -struct sources sources(struct journal *const j) { - struct sources s - = { sourcelist_array(), pair_map_table(), source_tree() }; - struct journal_iterator it; - union date32 k; - const char *v; - assert(j); - { /* Null is the first item for convenience, (TABLE_DEFAULT). */ - struct source *nul; - if(!(nul = sourcelist_array_new(&s.list))) goto catch; - nul->name.a = nul->name.b = nul->desc.a = nul->desc.b = 0; - } - it = journal_iterator(j); - while(journal_next(&it, &k, &v)) if(!scan(k, v, &s)) goto catch; - fprintf(stderr, "List of sources: %s.\n" - "Mapped to indices: %s.\n" - "Date-line tree: %s.\n", sourcelist_array_to_string(&s.list), - pair_map_table_to_string(&s.map), source_tree_to_string(&s.dates)); - goto finally; -catch: - sources_(&s); -finally: - return s; -} - -int sources_is_empty(const struct sources *const s) - { return !s || !s->dates.root.node; } - -/** Lookup the last source in `range` in sources `s`. They are invalidated on - adding a source, (currently fine because we get all at once.) */ -const struct source *source_lookup(struct sources *const s, - const union line64 x) { - struct source_tree_iterator it; - assert(s); - it = source_tree_less(&s->dates, x); - /* If it's before all elements of the journal or is not on the same date as - the source, this has no source, which is `list[0]` by . */ - return s->list.data + (source_tree_has_element(&it) - && x.date.u32 == source_tree_key(&it).date.u32 - ? *source_tree_value(&it) : 0); -} diff --git a/test/test_kjv.c b/test/test_kjv.c index 1c40971..cc5e93a 100644 --- a/test/test_kjv.c +++ b/test/test_kjv.c @@ -1,4 +1,4 @@ -#include "../src/kjvcite.h" +#include "../src/kjv.h" #include #include /* C99 */ #include diff --git a/test/test_source.c b/test/test_source.c index d24f7ad..012b326 100644 --- a/test/test_source.c +++ b/test/test_source.c @@ -1,4 +1,4 @@ -#include "../src/source.h" +#include "../src/scan.h" #include #include #include @@ -7,8 +7,8 @@ int main(void) { int success = EXIT_SUCCESS; errno = 0; /* `errno` is not set correctly to 0 in some debug situations. */ - struct journal j = journal("../journal"); - struct sources s = sources(&j); + struct journal jrnl = journal("../journal"); + struct scan scn = scan(&jrnl); if(errno) goto catch; const union line64 agenda = {{4,{{20,04,1996}}}}, @@ -16,17 +16,17 @@ int main(void) { noentry = {{3,{{1,6,1995}}}}; const struct source *source; - source = source_lookup(&s, agenda); + source = scan_source_lookup(&scn, agenda); printf("agenda: <%.*s>\n", (int)(source->name.b - source->name.a), source->name.a); assert(pair_is_string(source->name, "1995agenda")); - source = source_lookup(&s, before); + source = scan_source_lookup(&scn, before); printf("before: <%.*s>\n", (int)(source->name.b - source->name.a), source->name.a); assert(pair_is_string(source->name, 0)); - source = source_lookup(&s, noentry); + source = scan_source_lookup(&scn, noentry); printf("noentry: <%.*s>\n", (int)(source->name.b - source->name.a), source->name.a); assert(pair_is_string(source->name, 0)); @@ -35,7 +35,7 @@ int main(void) { catch: success = EXIT_FAILURE, perror("source"); finally: - sources_(&s); - journal_(&j); + scan_(&scn); + journal_(&jrnl); return success; }