Deleted old single projects.
This commit is contained in:
parent
c6ad32c2b7
commit
2fe62099db
11
Makefile
11
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"
|
||||
|
@ -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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
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 <<EOD\n"
|
||||
"# date, reg, sic, pic, source\n");
|
||||
struct flight_tree_iterator it = flights_iterator(&f);
|
||||
union line64 line;
|
||||
const struct flight *flight;
|
||||
while(flights_next(&it, &line, &flight)) {
|
||||
char datestr[12];
|
||||
date32_to_string(line.date, &datestr);
|
||||
const struct source *src = source_lookup(&s, line);
|
||||
assert(src); if(!src->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;
|
||||
}
|
@ -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 **);
|
239
src/flights.re.c
239
src/flights.re.c
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
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. */
|
||||
<skip> [^\n\x00] { continue; }
|
||||
<skip> "\x00" { why = "no newline at end of file"; goto catch; }
|
||||
<line> "\x00" { return 1; }
|
||||
<line, skip> "\n" => line { line++; continue; }
|
||||
<line> * :=> skip
|
||||
|
||||
/* Except this . . . */
|
||||
<line> "[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 */
|
||||
<glider_type> * { why = "type unrecognized"; goto catch; }
|
||||
<glider_reg> * { why = "reg unrecognized"; goto catch; }
|
||||
<glider_launch> * { why = "launch unrecognized"; goto catch; }
|
||||
<glider_how> * { why = "how unrecognized"; goto catch; }
|
||||
<glider_height> * { why = "height unrecognized"; goto catch; }
|
||||
<glider_landing> * { why = "landing unrecognized"; goto catch; }
|
||||
<glider_pilot> * { why = "pilot unrecognized"; goto catch; }
|
||||
<glider_dual> * { why = "dual unrecognized"; goto catch; }
|
||||
<glider_instr> * { why = "instr unrecognized"; goto catch; }
|
||||
<glider_remarks> * { why = "remarks unrecognized"; goto catch; }
|
||||
<glider_type> 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;
|
||||
}
|
||||
<glider_reg> ws* @s0 semitext+ @s1 ws* ";" => glider_launch
|
||||
{ flight->glider.reg.a = s0, flight->glider.reg.b = s1; continue; }
|
||||
<glider_launch> ws* @s0 airport @s1 ws* ";" => glider_how
|
||||
{ flight->glider.launch.a = s0, flight->glider.launch.b = s1;
|
||||
continue; }
|
||||
<glider_how> 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;
|
||||
}
|
||||
<glider_height> ws* @s0 natural @s1 "'" ws* ";" => glider_landing
|
||||
{ if(!pair_to_natural(s0, s1, &flight->glider.height_ft));
|
||||
continue; }
|
||||
<glider_landing> ws* @s0 airport @s1 ws* ";" => glider_pilot
|
||||
{ flight->glider.landing.a = s0, flight->glider.landing.b = s1;
|
||||
continue; }
|
||||
<glider_pilot> ws* ";" => glider_dual /* not PIC */
|
||||
{ flight->glider.pilot_min = 0; continue; }
|
||||
<glider_pilot> 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; }
|
||||
<glider_dual> ws* ";" => glider_instr
|
||||
{ flight->glider.dual_min = 0; continue; }
|
||||
<glider_dual> 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; }
|
||||
<glider_instr> ws* ";" => glider_remarks
|
||||
{ flight->glider.instr_min = 0; continue; }
|
||||
<glider_instr> 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; }
|
||||
<glider_remarks> ws* "\n" => line
|
||||
{ flight->glider.remarks.a = flight->glider.remarks.b = 0;
|
||||
flight = 0; line++; continue; }
|
||||
<glider_remarks> ws* @s0 glyph+ (ws+ glyph+)* @s1 "\n" => line
|
||||
{ flight->glider.remarks.a = s0, flight->glider.remarks.b = s1;
|
||||
flight = 0; line++; continue; }
|
||||
|
||||
/* And this . . . */
|
||||
<line> "[flight]" :=> flight_type
|
||||
/* type; registration; launch -- landing; pic; sic;
|
||||
single engine day dual; pilot; instrument simulated; actual; remarks */
|
||||
<flight_type> * { why = "type unrecognized"; goto catch; }
|
||||
<flight_reg> * { why = "reg unrecognized"; goto catch; }
|
||||
<flight_airports> * { why = "airports unrecognized"; goto catch; }
|
||||
<flight_pic> * { why = "pic unrecognized"; goto catch; }
|
||||
<flight_sic> * { why = "sic unrecognized"; goto catch; }
|
||||
<flight_dual> * { why = "dual time unrecognized"; goto catch; }
|
||||
<flight_pilot> * { why = "pilot time unrecognized"; goto catch; }
|
||||
<flight_ifrsim> * { why = "simulated ifr time unrecognized";
|
||||
goto catch; }
|
||||
<flight_ifr> * { why = "ifr time unrecognized"; goto catch; }
|
||||
<flight_remarks> * { why = "remarks unrecognized"; goto catch; }
|
||||
<flight_type> 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;
|
||||
}
|
||||
<flight_reg> ws* @s0 semitext+ @s1 ws* ";" => flight_airports
|
||||
{ flight->power.reg.a = s0, flight->power.reg.b = s1; continue; }
|
||||
<flight_airports> 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;
|
||||
}
|
||||
<flight_pic> ws* @s0 semitext+ (ws+ semitext+)* @s1 /* ws*? */";"
|
||||
=> flight_sic { flight->power.pilot.a = s0,
|
||||
flight->power.pilot.b = s1; continue; }
|
||||
<flight_sic> ws* ";" => flight_dual
|
||||
{ flight->power.copilot.a = flight->power.copilot.b = 0; continue; }
|
||||
<flight_sic> ws* @s0 semitext+ (ws+ semitext+)* @s1 ";"
|
||||
=> flight_dual { flight->power.copilot.a = s0,
|
||||
flight->power.copilot.b = s1; continue; }
|
||||
<flight_dual> ws* ";" => flight_pilot
|
||||
{ flight->power.dual_min = 0; continue; }
|
||||
<flight_dual> 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; }
|
||||
<flight_pilot> ws* ";" => flight_ifrsim
|
||||
{ flight->power.pilot_min = 0; continue; }
|
||||
<flight_pilot> 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; }
|
||||
<flight_ifrsim> ws* ";" => flight_ifr
|
||||
{ flight->power.ifrsim_min = 0; continue; }
|
||||
<flight_ifrsim> 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; }
|
||||
<flight_ifr> ws* ";" => flight_remarks
|
||||
{ flight->power.ifr_min = 0; continue; }
|
||||
<flight_ifr> 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; }
|
||||
<flight_remarks> ws* "\n" => line
|
||||
{ flight->power.remarks.a = flight->power.remarks.b = 0;
|
||||
flight = 0; line++; continue; }
|
||||
<flight_remarks> 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;
|
||||
}
|
121
src/kjv.h
121
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 <stdint.h> /* 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 <stddef.h> /* 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 <stddef.h> /* 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 *);
|
||||
|
554
src/kjv.re.c
554
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 <https://github.com/scrollmapper/bible_databases/master/txt/KJV/>
|
||||
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 <inttypes.h> /* C99 */
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h> /* opendir readdir closedir */
|
||||
#include <unistd.h> /* 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. */
|
||||
/** <https://nullprogram.com/blog/2018/07/31/>
|
||||
<https://github.com/skeeto/hash-prospector> 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 "<number>[*].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 <fn:looks_like_book_fn>. */
|
||||
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; }
|
||||
<line> [^[\]\n\x00]* "\n" { lex->line++; goto scan; }
|
||||
<line> "\x00" { return 0; }
|
||||
<line> "[" @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;
|
||||
}
|
||||
<verse> whitespace+ { goto scan; }
|
||||
<verse> @s0 word @s1 { lex->words++; goto scan; }
|
||||
<verse> "\n" { /*printf(" -> %u\n", lex->words);*/ lex->line++; return 1; }
|
||||
*/
|
||||
for( ; ; ) { /*!re2c /**/
|
||||
/* Default ignore. */
|
||||
<skip> [^\n\x00] { continue; }
|
||||
<skip> "\x00" { why = "no newline at end of file"; goto catch; }
|
||||
<line> "\x00" { return 1; }
|
||||
<line, skip> "\n" => line { line++; continue; }
|
||||
<line> * :=> skip
|
||||
}
|
||||
|
||||
/* Books. */
|
||||
<line> "Genesis" / lookat => book { book = Genesis; continue; }
|
||||
<line> "Exodus" / lookat => book { book = Exodus; continue; }
|
||||
<line> "Leviticus" / lookat => book { book = Leviticus; continue; }
|
||||
<line> "Numbers" / lookat => book { book = Numbers; continue; }
|
||||
<line> "Deuteronomy" / lookat => book { book = Deuteronomy; continue; }
|
||||
<line> "Joshua" / lookat => book { book = Joshua; continue; }
|
||||
<line> "Judges" / lookat => book { book = Judges; continue; }
|
||||
<line> "Ruth" / lookat => book { book = Ruth; continue; }
|
||||
<line> first "Samuel" / lookat => book { book = ISamuel; continue; }
|
||||
<line> second "Samuel" / lookat => book { book = IISamuel; continue; }
|
||||
<line> first "Kings" / lookat => book { book = IKings; continue; }
|
||||
<line> second "Kings" / lookat => book { book = IIKings; continue; }
|
||||
<line> first "Chronicles" / lookat
|
||||
=> book { book = IChronicles; continue; }
|
||||
<line> second "Chronicles" / lookat
|
||||
=> book { book = IIChronicles; continue; }
|
||||
<line> "Ezra" / lookat => book { book = Ezra; continue; }
|
||||
<line> "Nehemiah" / lookat => book { book = Nehemiah; continue; }
|
||||
<line> "Esther" / lookat => book { book = Esther; continue; }
|
||||
<line> "Job" / lookat => book { book = Job; continue; }
|
||||
<line> "Psalms" / lookat => book { book = Psalms; continue; }
|
||||
<line> "Proverbs" / lookat => book { book = Proverbs; continue; }
|
||||
<line> "Ecclesiastes" / lookat
|
||||
=> book { book = Ecclesiastes; continue; }
|
||||
<line> "Song of Solomon" / lookat
|
||||
=> book { book = Song_of_Solomon; continue; }
|
||||
<line> "Isaiah" / lookat => book { book = Isaiah; continue; }
|
||||
<line> "Jeremiah" / lookat => book { book = Jeremiah; continue; }
|
||||
<line> "Lamentations" / lookat
|
||||
=> book { book = Lamentations; continue; }
|
||||
<line> "Ezekiel" / lookat => book { book = Ezekiel; continue; }
|
||||
<line> "Daniel" / lookat => book { book = Daniel; continue; }
|
||||
<line> "Hosea" / lookat => book { book = Hosea; continue; }
|
||||
<line> "Joel" / lookat => book { book = Joel; continue; }
|
||||
<line> "Amos" / lookat => book { book = Amos; continue; }
|
||||
<line> "Obadiah" / lookat => book { book = Obadiah; continue; }
|
||||
<line> "Jonah" / lookat => book { book = Jonah; continue; }
|
||||
<line> "Micah" / lookat => book { book = Micah; continue; }
|
||||
<line> "Nahum" / lookat => book { book = Nahum; continue; }
|
||||
<line> "Habakkuk" / lookat => book { book = Habakkuk; continue; }
|
||||
<line> "Zephaniah" / lookat => book { book = Zephaniah; continue; }
|
||||
<line> "Haggai" / lookat => book { book = Haggai; continue; }
|
||||
<line> "Zechariah" / lookat => book { book = Zechariah; continue; }
|
||||
<line> "Malachi" / lookat => book { book = Malachi; continue; }
|
||||
<line> "Matthew" / lookat => book { book = Matthew; continue; }
|
||||
<line> "Mark" / lookat => book { book = Mark; continue; }
|
||||
<line> "Luke" / lookat => book { book = Luke; continue; }
|
||||
<line> "John" / lookat => book { book = John; continue; }
|
||||
<line> "Acts" / lookat => book { book = Acts; continue; }
|
||||
<line> "Romans" / lookat => book { book = Romans; continue; }
|
||||
<line> first "Corinthians" / lookat
|
||||
=> book { book = ICorinthians; continue; }
|
||||
<line> second "Corinthians" / lookat
|
||||
=> book { book = IICorinthians; continue; }
|
||||
<line> "Galatians" / lookat => book { book = Galatians; continue; }
|
||||
<line> "Ephesians" / lookat => book { book = Ephesians; continue; }
|
||||
<line> "Philippians" / lookat => book { book = Philippians; continue; }
|
||||
<line> "Colossians" / lookat => book { book = Colossians; continue; }
|
||||
<line> first "Thessalonians" / lookat
|
||||
=> book { book = IThessalonians; continue; }
|
||||
<line> second "Thessalonians" / lookat
|
||||
=> book { book = IIThessalonians; continue; }
|
||||
<line> first "Timothy" / lookat => book { book = ITimothy; continue; }
|
||||
<line> second "Timothy" / lookat => book { book = IITimothy; continue; }
|
||||
<line> "Titus" / lookat => book { book = Titus; continue; }
|
||||
<line> "Philemon" / lookat => book { book = Philemon; continue; }
|
||||
<line> "Hebrews" / lookat => book { book = Hebrews; continue; }
|
||||
<line> "James" / lookat => book { book = James; continue; }
|
||||
<line> first "Peter" / lookat => book { book = IPeter; continue; }
|
||||
<line> second "Peter" / lookat => book { book = IIPeter; continue; }
|
||||
<line> first "John" / lookat => book { book = IJohn; continue; }
|
||||
<line> second "John" / lookat => book { book = IIJohn; continue; }
|
||||
<line> third "John" / lookat => book { book = IIIJohn; continue; }
|
||||
<line> "Jude" / lookat => book { book = Jude; continue; }
|
||||
<line> "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. */
|
||||
<book> * { why = "default unrecognized"; goto catch; }
|
||||
/* 19:15a, just ignore the a. */
|
||||
<book> 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;
|
||||
}
|
||||
<book> "-" @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;
|
||||
}
|
||||
<book> 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 <<EOD\n"
|
||||
"# date, verse, words, set / %zu, source\n", no_total);
|
||||
while(kjv_line_next(&it, &line, &range)) {
|
||||
char citestr[12], datestr[12];
|
||||
size_t words = 0, newwords = 0;
|
||||
const struct source *src = source_lookup(srcs, line); /* Source. */
|
||||
if(!src || !src->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; }
|
||||
|
109
src/kjvcite.h
109
src/kjvcite.h
@ -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 <stdint.h> /* 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 <stddef.h> /* 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 *);
|
231
src/kjvcite.re.c
231
src/kjvcite.re.c
@ -1,231 +0,0 @@
|
||||
/** Run with a `kjv` sub-directory. Two functionaries: counts all words from
|
||||
verses <https://github.com/scrollmapper/bible_databases/master/txt/KJV/>
|
||||
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 <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <dirent.h> /* opendir readdir closedir */
|
||||
#include <unistd.h> /* 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. */
|
||||
/** <https://nullprogram.com/blog/2018/07/31/>
|
||||
<https://github.com/skeeto/hash-prospector> 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 "<number>[*].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 <fn:looks_like_book_fn>. */
|
||||
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; }
|
||||
<line> [^[\]\n\x00]* "\n" { lex->line++; goto scan; }
|
||||
<line> "\x00" { return 0; }
|
||||
<line> "[" @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;
|
||||
}
|
||||
<verse> whitespace+ { goto scan; }
|
||||
<verse> @s0 word @s1 { lex->words++; goto scan; }
|
||||
<verse> "\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; }
|
@ -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 *);
|
||||
|
@ -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);
|
||||
|
@ -1,4 +0,0 @@
|
||||
#include "helper.h"
|
||||
#include "flight.h"
|
||||
|
||||
int flight_scan(union date32, const char *, struct flight_tree *);
|
@ -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 <inttypes.h> /* C99 */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
/*!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. */
|
||||
<skip> [^\n\x00] { continue; }
|
||||
<skip> "\x00" { why = "no newline at end of file"; goto catch; }
|
||||
<line> "\x00" { return 1; }
|
||||
<line, skip> "\n" => line { line++; continue; }
|
||||
<line> * :=> skip
|
||||
|
||||
/* Except these two. */
|
||||
<line> "[glider]" :=> glider
|
||||
<line> "[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 */
|
||||
<glider, flight> * { 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;
|
||||
}
|
30
src/scores.h
30
src/scores.h
@ -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 *);
|
282
src/scores.re.c
282
src/scores.re.c
@ -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 <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
/* 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. */
|
||||
<skip> [^\n\x00] { continue; }
|
||||
<skip> "\x00" { why = "no newline at end of file"; goto catch; }
|
||||
<line> "\x00" { return 1; }
|
||||
<line, skip> "\n" => line { line++; continue; }
|
||||
<line> * :=> skip
|
||||
<line> "::" / [^:] :=> score
|
||||
|
||||
<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. */
|
||||
<score> @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. */
|
||||
<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;
|
||||
}
|
||||
<score_name> * { why = "name unrecognized"; goto catch; }
|
||||
<score_date> * { why = "date unrecognized"; goto catch; }
|
||||
<score_edges> * { why = "edges unrecognized"; goto catch; }
|
||||
<score_name> ws* @s0 semitext+ (" " semitext+)* @s1 /* ws* */ ";"
|
||||
=> score_date {
|
||||
assert(score);
|
||||
score->name.a = s0, score->name.b = s1;
|
||||
}
|
||||
<score_date> ws* "~"? @s0 date ws* ";" => score_edges {
|
||||
assert(score);
|
||||
if(!pair_to_date(s0, &score->date)) goto catch;
|
||||
}
|
||||
<score_edges> 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 <<EOD\n"
|
||||
"# date, key, key score\n");
|
||||
while(score_tree_next(&it)) {
|
||||
line = score_tree_key(&it);
|
||||
score = scrs->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 <stdlib.h>
|
||||
|
||||
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;
|
||||
}
|
28
src/source.h
28
src/source.h
@ -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 <stddef.h> /* 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);
|
170
src/source.re.c
170
src/source.re.c
@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
/* `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. */
|
||||
<skip> [^\n\x00] { continue; }
|
||||
<skip> "\x00" { why = "no newline at end of file"; goto catch; }
|
||||
<line> "\x00" { return 1; }
|
||||
<line, skip> "\n" => line { line++; continue; }
|
||||
<line> * :=> skip
|
||||
<line> "--" / [^-] :=> source
|
||||
|
||||
<source> * { why = "default source unrecognized"; goto catch; }
|
||||
<source> @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. */
|
||||
<source> @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 <fn:sources>. */
|
||||
return s->list.data + (source_tree_has_element(&it)
|
||||
&& x.date.u32 == source_tree_key(&it).date.u32
|
||||
? *source_tree_value(&it) : 0);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#include "../src/kjvcite.h"
|
||||
#include "../src/kjv.h"
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h> /* C99 */
|
||||
#include <stdlib.h>
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "../src/source.h"
|
||||
#include "../src/scan.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user