/** @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/source.h" #include "../src/kjv.h" #include "../src/pair.h" #include /* C99 */ #include #include #include #include #include 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" /*!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; /*!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]*; 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") " "?; */ 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; } /* 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); } return 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)) { 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; } 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); } 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); 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; }