Compiles.
This commit is contained in:
parent
4db31489c2
commit
aebeba65f7
6
Makefile
6
Makefile
@ -39,9 +39,9 @@ default: $(projects)
|
|||||||
bin/test-text: build/text.o build/test_text.o
|
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-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-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/kjv.o build/test_kjv.o
|
bin/test-kjv: build/text.o build/pair.o build/kjvcount.o build/test_kjv.o
|
||||||
bin/kjv: build/text.o build/pair.o build/journal.o build/kjv.o build/scan_kjv.o
|
bin/kjv: build/text.o build/pair.o build/journal.o build/kjvcount.o build/kjv.o
|
||||||
bin/flight: build/text.o build/pair.o build/journal.o build/source.o build/flight.o build/flighthours.o
|
bin/flight: build/text.o build/pair.o build/journal.o build/source.o build/flights.o build/flighthours.o
|
||||||
|
|
||||||
bin/%:
|
bin/%:
|
||||||
@echo "\033[1;36mlinking $@\033[0m"
|
@echo "\033[1;36mlinking $@\033[0m"
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
Date _vs_ hours flown. */
|
Date _vs_ hours flown. */
|
||||||
|
|
||||||
#include "journal.h"
|
#include "journal.h"
|
||||||
#include "flight.h"
|
#include "flights.h"
|
||||||
#include "source.h"
|
#include "source.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#if defined BASE \
|
#if defined BASE \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- base */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- base */
|
||||||
#define LAUNCH_TYPE \
|
#define LAUNCH_TYPE \
|
||||||
X(MotorCarTow),\
|
X(MotorCarTow),\
|
||||||
X(Winch),\
|
X(Winch),\
|
||||||
@ -47,8 +47,8 @@ struct flight {
|
|||||||
#endif /* base --> */
|
#endif /* base --> */
|
||||||
|
|
||||||
|
|
||||||
#if defined GENERIC \
|
#if defined PRIVATE \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- generic */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- generic */
|
||||||
struct tree_flight_node;
|
struct tree_flight_node;
|
||||||
struct tree_flight_tree { struct tree_flight_node *node; unsigned height; };
|
struct tree_flight_tree { struct tree_flight_node *node; unsigned height; };
|
||||||
struct flight_tree { struct tree_flight_tree root; };
|
struct flight_tree { struct tree_flight_tree root; };
|
||||||
@ -61,7 +61,7 @@ struct flight_tree_iterator { struct tree_flight_iterator _; };
|
|||||||
|
|
||||||
|
|
||||||
#if defined PROTO \
|
#if defined PROTO \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- proto */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- proto */
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
struct flights { struct flight_tree _; };
|
struct flights { struct flight_tree _; };
|
||||||
struct flights_iterator { struct flight_tree_iterator _; };
|
struct flights_iterator { struct flight_tree_iterator _; };
|
||||||
@ -76,8 +76,8 @@ int flights_next(struct flights_iterator *, union line64 *,
|
|||||||
#ifdef BASE
|
#ifdef BASE
|
||||||
#undef BASE
|
#undef BASE
|
||||||
#endif
|
#endif
|
||||||
#ifdef GENERIC
|
#ifdef PRIVATE
|
||||||
#undef GENERIC
|
#undef PRIVATE
|
||||||
#endif
|
#endif
|
||||||
#ifdef PROTO
|
#ifdef PROTO
|
||||||
#undef PROTO
|
#undef PROTO
|
@ -2,7 +2,7 @@
|
|||||||
[MIT License](https://opensource.org/licenses/MIT).
|
[MIT License](https://opensource.org/licenses/MIT).
|
||||||
@std C11 */
|
@std C11 */
|
||||||
#define BASE
|
#define BASE
|
||||||
#include "../src/flight.h" /* base */
|
#include "../src/flights.h" /* base */
|
||||||
#include "../src/journal.h"
|
#include "../src/journal.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -22,7 +22,7 @@ static int flight_compare(const union line64 a, const union line64 b)
|
|||||||
|
|
||||||
|
|
||||||
#define PROTO
|
#define PROTO
|
||||||
#include "../src/flight.h" /* proto */
|
#include "../src/flights.h" /* proto */
|
||||||
|
|
||||||
|
|
||||||
/*!conditions:re2c*/
|
/*!conditions:re2c*/
|
@ -1,5 +1,5 @@
|
|||||||
#if defined BASE \
|
#if defined BASE \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- base */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- base */
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h> /* C99 */
|
#include <stdint.h> /* C99 */
|
||||||
/** Assumes: reverse ordering of byte-fields; unsigned is defined; C11 or GNU
|
/** Assumes: reverse ordering of byte-fields; unsigned is defined; C11 or GNU
|
||||||
@ -16,8 +16,8 @@ union line64 {
|
|||||||
#endif /* base --> */
|
#endif /* base --> */
|
||||||
|
|
||||||
|
|
||||||
#if defined GENERIC \
|
#if defined PRIVATE \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- generic */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- generic */
|
||||||
struct tree_day_node;
|
struct tree_day_node;
|
||||||
struct tree_day_tree { struct tree_day_node *node; unsigned height; };
|
struct tree_day_tree { struct tree_day_node *node; unsigned height; };
|
||||||
struct day_tree { struct tree_day_tree root; };
|
struct day_tree { struct tree_day_tree root; };
|
||||||
@ -29,7 +29,7 @@ struct day_tree_iterator { struct tree_day_iterator _; };
|
|||||||
|
|
||||||
|
|
||||||
#if defined PROTO \
|
#if defined PROTO \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- proto */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- proto */
|
||||||
#include "text.h"
|
#include "text.h"
|
||||||
struct journal { struct day_tree days; struct text backing; };
|
struct journal { struct day_tree days; struct text backing; };
|
||||||
struct journal_iterator { struct day_tree_iterator _; };
|
struct journal_iterator { struct day_tree_iterator _; };
|
||||||
@ -44,8 +44,8 @@ int journal_next(struct journal_iterator *, union date32 *, const char **);
|
|||||||
#ifdef BASE
|
#ifdef BASE
|
||||||
#undef BASE
|
#undef BASE
|
||||||
#endif
|
#endif
|
||||||
#ifdef GENERIC
|
#ifdef PRIVATE
|
||||||
#undef GENERIC
|
#undef PRIVATE
|
||||||
#endif
|
#endif
|
||||||
#ifdef PROTO
|
#ifdef PROTO
|
||||||
#undef PROTO
|
#undef PROTO
|
||||||
|
169
src/kjv.h
169
src/kjv.h
@ -1,113 +1,82 @@
|
|||||||
#if defined BASE \
|
#if defined BASE \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- base */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- base */
|
||||||
#define BOOKS \
|
#ifdef BASE
|
||||||
X(Genesis),\
|
#define PUSH_BASE
|
||||||
X(Exodus),\
|
#undef BASE
|
||||||
X(Leviticus),\
|
#endif
|
||||||
X(Numbers),\
|
#ifdef PRIVATE
|
||||||
X(Deuteronomy),\
|
#define PUSH_PRIVATE
|
||||||
X(Joshua),\
|
#undef PRIVATE
|
||||||
X(Judges),\
|
#endif
|
||||||
X(Ruth),\
|
#ifdef PROTO
|
||||||
X(ISamuel),\
|
#define PUSH_PROTO
|
||||||
X(IISamuel),\
|
#undef PROTO
|
||||||
X(IKings),\
|
#endif
|
||||||
X(IIKings),\
|
#include "kjvcount.h" /* Uses same conflicting system. */
|
||||||
X(IChronicles),\
|
#ifdef PUSH_BASE
|
||||||
X(IIChronicles),\
|
#undef PUSH_BASE
|
||||||
X(Ezra),\
|
#define BASE
|
||||||
X(Nehemiah),\
|
#endif
|
||||||
X(Esther),\
|
#ifdef PUSH_PRIVATE
|
||||||
X(Job),\
|
#undef PUSH_PRIVATE
|
||||||
X(Psalms),\
|
#define PRIVATE
|
||||||
X(Proverbs),\
|
#endif
|
||||||
X(Ecclesiastes),\
|
#ifdef PUSH_PROTO
|
||||||
X(Song_of_Solomon),\
|
#undef PUSH_PROTO
|
||||||
X(Isaiah),\
|
#define PROTO
|
||||||
X(Jeremiah),\
|
#endif
|
||||||
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;
|
|
||||||
};
|
|
||||||
void kjvcite_to_string(const union kjvcite, char (*)[12]);
|
|
||||||
#endif /* base --> */
|
#endif /* base --> */
|
||||||
|
|
||||||
|
|
||||||
#if defined GENERIC \
|
#if defined PRIVATE \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- generic */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- private */
|
||||||
struct table_kjvset_bucket;
|
struct tree_kjvline_node;
|
||||||
struct kjvset_table {
|
struct tree_kjvline_tree { struct tree_kjvline_node *node; unsigned height; };
|
||||||
struct table_kjvset_bucket *buckets;
|
struct kjvline_tree { struct tree_kjvline_tree root; };
|
||||||
uint32_t log_capacity, size, top;
|
struct tree_kjvline_ref
|
||||||
|
{ struct tree_kjvline_node *node; unsigned height, idx; };
|
||||||
|
struct tree_kjvline_iterator {
|
||||||
|
struct tree_kjvline_tree *root; struct tree_kjvline_ref ref; int seen;
|
||||||
};
|
};
|
||||||
struct table_verse_bucket;
|
struct kjvline_tree_iterator { struct tree_kjvline_iterator _; };
|
||||||
struct verse_table {
|
#endif /* private --> */
|
||||||
struct table_verse_bucket *buckets;
|
|
||||||
uint32_t log_capacity, size, top;
|
|
||||||
};
|
|
||||||
#endif /* generic --> */
|
|
||||||
|
|
||||||
|
|
||||||
#if defined PROTO \
|
#if defined PROTO \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- proto */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- proto */
|
||||||
|
#ifdef BASE
|
||||||
|
#error BASE!!!
|
||||||
|
#endif
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
struct kjv {
|
struct kjv {
|
||||||
|
struct kjvline_tree line;
|
||||||
struct kjvset_table set;
|
struct kjvset_table set;
|
||||||
struct verse_table verses;
|
struct count_table count;
|
||||||
struct { size_t total, cumulative, set, verse; } words;
|
struct { size_t total, cumulative, set, verse; } words;
|
||||||
};
|
};
|
||||||
|
void kjv_line_(struct kjvline_tree *);
|
||||||
|
struct kjvline_tree kjv_line(struct journal *);
|
||||||
|
int kjv_line_is_empty(const struct kjvline_tree *);
|
||||||
|
const char *kjv_line_to_string(const struct kjvline_tree *);
|
||||||
|
struct kjvline_tree_iterator kjv_line_iterator(struct kjvline_tree *);
|
||||||
|
int kjv_line_next(struct kjvline_tree_iterator *, union line64 *,
|
||||||
|
const struct kjvrange **);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
const char *kjvline_to_string(const struct kjvline *);
|
||||||
|
struct kjvline_iterator kjvline_iterator(struct kjvline *);
|
||||||
|
int kjvline_next(struct kjvline_iterator *, union line64 *,
|
||||||
|
const struct kjvline **);
|
||||||
|
|
||||||
|
struct flights_iterator { struct flight_tree_iterator _; };
|
||||||
|
struct flights flights(/*const*/ struct journal *);
|
||||||
|
void flights_(struct flights *);
|
||||||
|
const char *flights_to_string(const struct flights *);
|
||||||
|
struct flights_iterator flights_iterator(struct flights *);
|
||||||
|
int flights_next(struct flights_iterator *, union line64 *,
|
||||||
|
const struct flight **);
|
||||||
|
|
||||||
|
|
||||||
struct kjv kjv(void);
|
struct kjv kjv(void);
|
||||||
void kjv_(struct kjv *);
|
void kjv_(struct kjv *);
|
||||||
int kjv_is_empty(const struct kjv *const kjv);
|
int kjv_is_empty(const struct kjv *const kjv);
|
||||||
@ -115,13 +84,15 @@ int kjv_is_empty(const struct kjv *const kjv);
|
|||||||
int kjv_add(struct kjv *const kjv, const union kjvcite cite);
|
int kjv_add(struct kjv *const kjv, const union kjvcite cite);
|
||||||
const char *kjv_to_string(const struct kjv *const kjv);
|
const char *kjv_to_string(const struct kjv *const kjv);
|
||||||
const char *kjv_set_to_string(const struct kjv *const kjv);
|
const char *kjv_set_to_string(const struct kjv *const kjv);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* proto --> */
|
#endif /* proto --> */
|
||||||
|
|
||||||
#ifdef BASE
|
#ifdef BASE
|
||||||
#undef BASE
|
#undef BASE
|
||||||
#endif
|
#endif
|
||||||
#ifdef GENERIC
|
#ifdef PRIVATE
|
||||||
#undef GENERIC
|
#undef PRIVATE
|
||||||
#endif
|
#endif
|
||||||
#ifdef PROTO
|
#ifdef PROTO
|
||||||
#undef PROTO
|
#undef PROTO
|
||||||
|
510
src/kjv.re.c
510
src/kjv.re.c
@ -1,244 +1,314 @@
|
|||||||
/** Run with a `KJV` sub-directory,
|
/** @license 2022 Neil Edelman, distributed under the terms of the
|
||||||
<https://github.com/scrollmapper/bible_databases/master/txt/KJV/>, outputs a
|
[MIT License](https://opensource.org/licenses/MIT).
|
||||||
`gperf` file that has all the the words of all the verses in `kjv.h` format.
|
|
||||||
@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/journal.h"
|
||||||
#define BASE
|
#define BASE
|
||||||
#include "../src/kjv.h" /* Just the base data. */
|
#include "../src/kjv.h"
|
||||||
#include "../src/text.h"
|
|
||||||
#include "../src/pair.h"
|
#include "../src/pair.h"
|
||||||
#include <inttypes.h>
|
#include <inttypes.h> /* C99 */
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <dirent.h> /* opendir readdir closedir */
|
#include <limits.h>
|
||||||
#include <unistd.h> /* chdir (POSIX) (because I'm lazy) */
|
|
||||||
/* #include <cmph.h> No; overkill. */
|
|
||||||
|
|
||||||
|
|
||||||
void kjvcite_to_string(const union kjvcite x, char (*const a)[12])
|
static void kjvline_to_string(const union line64 line, const struct kjvrange *u,
|
||||||
{ sprintf(*a, "%.4s%" PRIu32 ":%" PRIu32,
|
char (*const a)[12]) { (void)u; date32_to_string(line.date, a); }
|
||||||
kjv_book_string[x.book < KJV_BOOK_SIZE ? x.book : KJV_BOOK_SIZE],
|
static int kjvline_compare(const union line64 a, const union line64 b)
|
||||||
x.chapter % 1000, x.verse % 1000); }
|
{ return a.u64 > b.u64; }
|
||||||
|
#define TREE_NAME kjvline
|
||||||
/* Reversible hash map. */
|
#define TREE_KEY union line64
|
||||||
/** <https://nullprogram.com/blog/2018/07/31/>
|
#define TREE_VALUE struct kjvrange
|
||||||
<https://github.com/skeeto/hash-prospector> on `x`. */
|
#define TREE_COMPARE
|
||||||
static uint32_t lowbias32(uint32_t x) {
|
#define TREE_TO_STRING
|
||||||
x ^= x >> 16;
|
#include "../src/tree.h"
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
#include "../src/table.h"
|
|
||||||
|
|
||||||
/* Derived information on verse word count. */
|
|
||||||
static uint32_t verse_hash(const union kjvcite x) { return kjvset_hash(x); }
|
|
||||||
static union kjvcite verse_unhash(const uint32_t x) { return kjvset_unhash(x); }
|
|
||||||
static void verse_to_string(const union kjvcite x, const unsigned count,
|
|
||||||
char (*const a)[12]) { (void)count; kjvset_to_string(x, a); }
|
|
||||||
#define TABLE_NAME verse
|
|
||||||
#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
|
|
||||||
#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; }
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define PROTO
|
#define PROTO
|
||||||
#include "../src/kjv.h" /* Just the kjv and prototypes. */
|
#include "../src/kjv.h" /* proto */
|
||||||
|
|
||||||
/** Frees `kjv`. */
|
|
||||||
void kjv_(struct kjv *const kjv) {
|
/*!conditions:re2c*/
|
||||||
if(!kjv) return;
|
|
||||||
kjvset_table_(&kjv->set);
|
static int scan(union date32 date, const char *const buffer,
|
||||||
verse_table_(&kjv->verses);
|
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. */
|
||||||
|
<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; }
|
||||||
|
|
||||||
|
/* 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_try(lines, key, &value)) {
|
||||||
|
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, "%s\n"
|
||||||
|
"%s line %zu: %s.\n", buffer, datestr, line, why);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Loads 66 files from the "kjv/" directory. Prints out something if it
|
void kjv_line_(struct kjvline_tree *const f) { kjvline_tree_(f); }
|
||||||
doesn't work, but does not call `perror` or reset `errno`. Use
|
|
||||||
<fn:kjv_is_valid> to tell. */
|
|
||||||
struct kjv kjv(void) {
|
|
||||||
const char *const dir_kjv = "kjv";
|
|
||||||
struct text backing = text();
|
|
||||||
struct kjv kjv = { 0 };
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* For all files in directory KJV with <#>*.txt, read into backing. */
|
struct kjvline_tree kjv_line(struct journal *const j) {
|
||||||
if(chdir(dir_kjv) == -1 || (is_in_kjv = 1, !(dir = opendir("."))))
|
struct kjvline_tree lines;
|
||||||
goto catch;
|
struct journal_iterator it;
|
||||||
while((de = readdir(dir))) {
|
union date32 date;
|
||||||
unsigned ordinal;
|
const char *text;
|
||||||
char *unstable_backing;
|
assert(j);
|
||||||
if(!looks_like_book_fn(de->d_name, &ordinal)) continue;
|
lines = kjvline_tree();
|
||||||
/*fprintf(stderr, "<%s> ordinal: %u\n", de->d_name, ordinal);*/
|
it = journal_iterator(j);
|
||||||
if(ordinal < 1 || ordinal > KJV_BOOK_SIZE)
|
while(journal_next(&it, &date, &text))
|
||||||
{ errno = ERANGE; goto catch; } /* Not in range. */
|
if(!scan(date, text, &lines)) goto catch;
|
||||||
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.a.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.a.data + build[b].offset);
|
|
||||||
while(lex_next_verse(&x)) {
|
|
||||||
const union kjvcite cite
|
|
||||||
= { .book = b, .chapter = x.chapter, .verse = x.verse };
|
|
||||||
unsigned *words;
|
|
||||||
switch(verse_table_assign(&kjv.verses, 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, kjv.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;
|
goto finally;
|
||||||
catch:
|
catch:
|
||||||
if(de) fprintf(stderr, "While reading %s/%s.\n", dir_kjv, de->d_name);
|
kjv_line_(&lines);
|
||||||
else fprintf(stderr, "In directory %s/.\n", dir_kjv);
|
|
||||||
recatch:
|
|
||||||
kjv_(&kjv);
|
|
||||||
finally:
|
finally:
|
||||||
if(dir) { if(closedir(dir)) { dir = 0; goto recatch; } dir = 0; }
|
return lines;
|
||||||
if(is_in_kjv && (is_in_kjv = 0, chdir("..") == -1)) goto recatch;
|
|
||||||
text_(&backing);
|
|
||||||
return kjv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Has loaded properly? Otherwise, probably `errno` is set. */
|
int kjv_line_is_empty(const struct kjvline_tree *const lines)
|
||||||
int kjv_is_empty(const struct kjv *const kjv)
|
{ return !lines || !lines->root.node; }
|
||||||
{ return !kjv || !kjv->verses.buckets; }
|
|
||||||
|
|
||||||
/** Adds `cite` to `kjv` if not present. Only used in test.
|
const char *kjv_line_to_string(const struct kjvline_tree *const kl)
|
||||||
@return Is the kjv still valid. */
|
{ return kjvline_tree_to_string(kl); }
|
||||||
int kjv_add(struct kjv *const kjv, const union kjvcite cite) {
|
|
||||||
if(!kjv) return 0;
|
struct kjvline_tree_iterator kjv_line_iterator(struct kjvline_tree *const kl)
|
||||||
kjv->words.verse = verse_table_get(&kjv->verses, cite);
|
{ return kjvline_tree_iterator(kl); }
|
||||||
kjv->words.cumulative += kjv->words.verse;
|
|
||||||
switch(kjvset_table_try(&kjv->set, cite)) {
|
int kjv_line_next(struct kjvline_tree_iterator *const it, union line64 *const k,
|
||||||
case TABLE_ERROR: return 0;
|
const struct kjvrange **const v) {
|
||||||
case TABLE_ABSENT: kjv->words.set += kjv->words.verse; /* Sic. */
|
assert(it && k && v);
|
||||||
case TABLE_PRESENT: break;
|
if(!kjvline_tree_next(it)) return 0;
|
||||||
}
|
*k = kjvline_tree_key(it);
|
||||||
|
*v = kjvline_tree_value(it);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *kjv_to_string(const struct kjv *const kjv)
|
|
||||||
{ return kjv ? verse_table_to_string(&kjv->verses) : ""; }
|
|
||||||
|
|
||||||
const char *kjv_set_to_string(const struct kjv *const kjv)
|
|
||||||
{ return kjv ? kjvset_table_to_string(&kjv->set) : 0; }
|
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
char citestr[12], datestr[12];
|
||||||
|
const struct source *src = source_lookup(&s, line);
|
||||||
|
assert(src); if(!src->name.a) { errno = EDOM; goto catch; }
|
||||||
|
kjvcite_to_string(cite, &citestr);
|
||||||
|
for( ; ; cite.verse++) {
|
||||||
|
if(!kjv_add(kj, cite)) return 0;
|
||||||
|
if(!verse_end || verse_end <= cite.verse) break;
|
||||||
|
}
|
||||||
|
date32_to_string(date, &datestr);
|
||||||
|
printf("%s\t%zu\t%zu\t%zu\t# ",
|
||||||
|
datestr, kj->words.verse, kj->words.set, kj->words.cumulative);
|
||||||
|
if(verse_end) {
|
||||||
|
printf("%s-%" PRIu32 "\n", citestr, verse_end);
|
||||||
|
} else {
|
||||||
|
printf("%s\n", citestr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
int success = EXIT_SUCCESS;
|
||||||
|
const char *reason = "unknown";
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
struct kjvcount count = kjvcount();
|
||||||
|
if(kjvcount_is_empty(&count)) { reason = "kjv failed to load"; goto catch; }
|
||||||
|
|
||||||
|
struct journal j = journal();
|
||||||
|
if(journal_is_empty(&j)) { reason = "journal failed to load"; goto catch; }
|
||||||
|
fprintf(stderr, "Journal: %s.\n", journal_to_string(&j));
|
||||||
|
|
||||||
|
struct kjvline_tree lines = kjv_line(&j);
|
||||||
|
if(kjv_line_is_empty(&lines)) { reason = "parsing failed"; goto catch; }
|
||||||
|
fprintf(stderr, "Lines: %s.\n", kjv_line_to_string(&lines));
|
||||||
|
|
||||||
|
struct journal_iterator it;
|
||||||
|
union date32 k;
|
||||||
|
const char *v;
|
||||||
|
|
||||||
|
printf("set term postscript eps enhanced\n"
|
||||||
|
"set output \"kjv.eps\"\n"
|
||||||
|
"$Data <<EOD\n"
|
||||||
|
"# date\tverse\tset\tcumulative / %zu\n", count.words.total);
|
||||||
|
it = journal_iterator(&j);
|
||||||
|
while(journal_next(&it, &k, &v)) if(!scan(k, v, &lines)) goto catch;
|
||||||
|
printf("EOD\n"
|
||||||
|
"set monochrome\n"
|
||||||
|
"set xdata time\n"
|
||||||
|
"set timefmt \"%%Y-%%m-%%d\"\n"
|
||||||
|
"set xtics format \"%%Y-%%m-%%d\" rotate by -30\n"
|
||||||
|
"set ylabel \"words in KJV\"\n"
|
||||||
|
"set format y \"%%g%%%%\"\n"
|
||||||
|
"set key top left\n"
|
||||||
|
"set grid\n"
|
||||||
|
"unset border\n"
|
||||||
|
"#set style fill solid 0.1 #pattern 5 (better, but restarts)\n"
|
||||||
|
"plot $Data using 1:($3)*100/%zu with fillsteps lw 2 title \"set\", \\\n"
|
||||||
|
"$Data using 1:($4)*100/%zu with steps lw 1 title \"cumulative\"\n",
|
||||||
|
count.words.total, count.words.total);
|
||||||
|
goto finally;
|
||||||
|
catch:
|
||||||
|
success = EXIT_FAILURE;
|
||||||
|
perror("journal");
|
||||||
|
fprintf(stderr, "Details: %s.\n", reason);
|
||||||
|
finally:
|
||||||
|
kjv_line_(&lines);
|
||||||
|
kjvcount_(&count);
|
||||||
|
journal_(&j);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
131
src/kjvcount.h
Normal file
131
src/kjvcount.h
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#if defined BASE \
|
||||||
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- base */
|
||||||
|
#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]);
|
||||||
|
#endif /* base --> */
|
||||||
|
|
||||||
|
|
||||||
|
#if defined PRIVATE \
|
||||||
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- private */
|
||||||
|
struct table_kjvset_bucket;
|
||||||
|
struct kjvset_table {
|
||||||
|
struct table_kjvset_bucket *buckets;
|
||||||
|
uint32_t log_capacity, size, top;
|
||||||
|
};
|
||||||
|
struct table_count_bucket;
|
||||||
|
struct count_table {
|
||||||
|
struct table_count_bucket *buckets;
|
||||||
|
uint32_t log_capacity, size, top;
|
||||||
|
};
|
||||||
|
#endif /* private --> */
|
||||||
|
|
||||||
|
|
||||||
|
#if defined PROTO \
|
||||||
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- proto */
|
||||||
|
#include <stddef.h>
|
||||||
|
struct kjvcount {
|
||||||
|
struct count_table verses;
|
||||||
|
struct { size_t total, cumulative, set; } words;
|
||||||
|
};
|
||||||
|
void kjvcount_(struct kjvcount *);
|
||||||
|
struct kjvcount kjvcount(void);
|
||||||
|
int kjvcount_is_empty(const struct kjvcount *);
|
||||||
|
const char *kjvcount_to_string(const struct kjvcount *);
|
||||||
|
struct kjvset_table kjv_set(void);
|
||||||
|
void kjv_set_(struct kjvset_table *);
|
||||||
|
int kjv_set_add(struct kjvset_table *const set,
|
||||||
|
struct kjvcount *const count, const union kjvcite cite);
|
||||||
|
const char *kjv_set_to_string(const struct kjvset_table *);
|
||||||
|
#endif /* proto --> */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef BASE
|
||||||
|
#undef BASE
|
||||||
|
#endif
|
||||||
|
#ifdef PRIVATE
|
||||||
|
#undef PRIVATE
|
||||||
|
#endif
|
||||||
|
#ifdef PROTO
|
||||||
|
#undef PROTO
|
||||||
|
#endif
|
249
src/kjvcount.re.c
Normal file
249
src/kjvcount.re.c
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
/** 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 */
|
||||||
|
|
||||||
|
#define BASE
|
||||||
|
#include "../src/kjvcount.h"
|
||||||
|
#include "../src/text.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) */
|
||||||
|
/* #include <cmph.h> No; overkill. */
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
#include "../src/table.h"
|
||||||
|
|
||||||
|
/* Derived information on verse word count. */
|
||||||
|
static uint32_t count_hash(const union kjvcite x) { return kjvset_hash(x); }
|
||||||
|
static union kjvcite count_unhash(const uint32_t x) { return kjvset_unhash(x); }
|
||||||
|
static void count_to_string(const union kjvcite x, const unsigned count,
|
||||||
|
char (*const a)[12]) { (void)count; kjvset_to_string(x, a); }
|
||||||
|
#define TABLE_NAME count
|
||||||
|
#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
|
||||||
|
#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; }
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define PROTO
|
||||||
|
#include "../src/kjvcount.h" /* Just the kjv and prototypes. */
|
||||||
|
|
||||||
|
/** Frees `kjv`. */
|
||||||
|
void kjvcount_(struct kjvcount *const count) {
|
||||||
|
if(!count) return;
|
||||||
|
count_table_(&count->verses);
|
||||||
|
count->words.total = count->words.cumulative = count->words.set = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Loads 66 files from the "kjv/" directory. Prints out something if it
|
||||||
|
doesn't work, but does not call `perror` or reset `errno`. Use
|
||||||
|
<fn:kjvcount_is_empty> to tell. */
|
||||||
|
struct kjvcount kjvcount(void) {
|
||||||
|
const char *const dir_kjv = "kjv";
|
||||||
|
struct text backing = text();
|
||||||
|
struct kjvcount count = {0};
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* 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.a.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.a.data + build[b].offset);
|
||||||
|
while(lex_next_verse(&x)) {
|
||||||
|
const union kjvcite cite
|
||||||
|
= { .book = b, .chapter = x.chapter, .verse = x.verse };
|
||||||
|
unsigned *words;
|
||||||
|
switch(count_table_assign(&count.verses, cite, &words)) {
|
||||||
|
case TABLE_PRESENT: fprintf(stderr, "[%u]%s %u:%u duplicated.\n",
|
||||||
|
b + 1, kjv_book_string[b], x.chapter, x.verse); errno = EDOM;
|
||||||
|
case TABLE_ERROR: goto catch;
|
||||||
|
case TABLE_ABSENT: break;
|
||||||
|
}
|
||||||
|
*words = x.words, count.words.total += x.words;
|
||||||
|
}
|
||||||
|
if(x.error) { fprintf(stderr, "[%u]%s on line %zu\n",
|
||||||
|
b + 1, kjv_book_string[b], x.line); goto catch; }
|
||||||
|
}
|
||||||
|
goto finally;
|
||||||
|
catch:
|
||||||
|
if(de) fprintf(stderr, "While reading %s/%s.\n", dir_kjv, de->d_name);
|
||||||
|
else fprintf(stderr, "In directory %s/.\n", dir_kjv);
|
||||||
|
recatch:
|
||||||
|
kjvcount_(&count);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Has loaded properly? Otherwise, probably `errno` is set. */
|
||||||
|
int kjvcount_is_empty(const struct kjvcount *const kjv)
|
||||||
|
{ return !kjv || !kjv->verses.buckets; }
|
||||||
|
|
||||||
|
const char *kjvcount_to_string(const struct kjvcount *const count)
|
||||||
|
{ return count ? count_table_to_string(&count->verses) : ""; }
|
||||||
|
|
||||||
|
struct kjvset_table kjv_set(void) { return kjvset_table(); }
|
||||||
|
void kjv_set_(struct kjvset_table *const set) { kjvset_table_(set); }
|
||||||
|
const char *kjv_set_to_string(const struct kjvset_table *const set)
|
||||||
|
{ return set ? kjvset_table_to_string(set) : 0; }
|
||||||
|
/** Adds `cite` to `kjv` if not present. Only used in test.
|
||||||
|
@return Is the kjv still valid. */
|
||||||
|
int kjv_set_add(struct kjvset_table *const set,
|
||||||
|
struct kjvcount *const count, const union kjvcite cite) {
|
||||||
|
size_t no_verse;
|
||||||
|
if(!set || !count) return 0;
|
||||||
|
no_verse = count_table_get(&count->verses, cite);
|
||||||
|
count->words.cumulative += no_verse;
|
||||||
|
switch(kjvset_table_try(set, cite)) {
|
||||||
|
case TABLE_ERROR: return 0;
|
||||||
|
case TABLE_ABSENT: count->words.set += no_verse; /* Sic. */
|
||||||
|
case TABLE_PRESENT: break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
@ -1,244 +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/kjv.h"
|
|
||||||
#include "../src/pair.h"
|
|
||||||
#include <inttypes.h> /* C99 */
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
/* Callback is stupid; separate into tree with line and verse, and then more
|
|
||||||
processing. */
|
|
||||||
typedef int (*scan_callback)(struct kjv *, union line64, union kjvcite,
|
|
||||||
uint32_t);
|
|
||||||
|
|
||||||
/*!conditions:re2c*/
|
|
||||||
|
|
||||||
static int scan(union date32 date, const char *const buffer,
|
|
||||||
const scan_callback callback, struct kjv *const kj) {
|
|
||||||
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 && kj);
|
|
||||||
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. */
|
|
||||||
<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; }
|
|
||||||
|
|
||||||
/* 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; }
|
|
||||||
union kjvcite cite
|
|
||||||
= { .book = book, .chapter = chapter, .verse = verse };
|
|
||||||
if(!callback(kj, date, cite, verse_end))
|
|
||||||
{ why = "add to set"; goto catch; }
|
|
||||||
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, "%s\n"
|
|
||||||
"%s line %zu: %s.\n", buffer, datestr, line, why);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int add_to_set(struct kjv *const kj, const union line64 line,
|
|
||||||
union kjvcite cite, const uint32_t verse_end) {
|
|
||||||
char citestr[12], datestr[12];
|
|
||||||
const struct source *src = source_lookup(&s, line);
|
|
||||||
assert(src); if(!src->name.a) { errno = EDOM; goto catch; }
|
|
||||||
kjvcite_to_string(cite, &citestr);
|
|
||||||
for( ; ; cite.verse++) {
|
|
||||||
if(!kjv_add(kj, cite)) return 0;
|
|
||||||
if(!verse_end || verse_end <= cite.verse) break;
|
|
||||||
}
|
|
||||||
date32_to_string(date, &datestr);
|
|
||||||
printf("%s\t%zu\t%zu\t%zu\t# ",
|
|
||||||
datestr, kj->words.verse, kj->words.set, kj->words.cumulative);
|
|
||||||
if(verse_end) {
|
|
||||||
printf("%s-%" PRIu32 "\n", citestr, verse_end);
|
|
||||||
} else {
|
|
||||||
printf("%s\n", citestr);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
int success = EXIT_SUCCESS;
|
|
||||||
const char *reason = "unknown";
|
|
||||||
errno = 0;
|
|
||||||
struct journal j;
|
|
||||||
struct journal_iterator it;
|
|
||||||
struct kjv bible = kjv();
|
|
||||||
if(kjv_is_empty(&bible)) { reason = "kjv failed to load"; goto catch; }
|
|
||||||
union date32 k;
|
|
||||||
const char *v;
|
|
||||||
j = journal();
|
|
||||||
if(journal_is_empty(&j)) { reason = "journal failed to load"; goto catch; }
|
|
||||||
fprintf(stderr, "Journal: %s.\n", journal_to_string(&j));
|
|
||||||
printf("set term postscript eps enhanced\n"
|
|
||||||
"set output \"kjv.eps\"\n"
|
|
||||||
"$Data <<EOD\n"
|
|
||||||
"# date\tverse\tset\tcumulative / %zu\n", bible.words.total);
|
|
||||||
it = journal_iterator(&j);
|
|
||||||
while(journal_next(&it, &k, &v))
|
|
||||||
if(!scan(k, v, &add_to_set, &bible)) goto catch;
|
|
||||||
printf("EOD\n"
|
|
||||||
"set monochrome\n"
|
|
||||||
"set xdata time\n"
|
|
||||||
"set timefmt \"%%Y-%%m-%%d\"\n"
|
|
||||||
"set xtics format \"%%Y-%%m-%%d\" rotate by -30\n"
|
|
||||||
"set ylabel \"words in KJV\"\n"
|
|
||||||
"set format y \"%%g%%%%\"\n"
|
|
||||||
"set key top left\n"
|
|
||||||
"set grid\n"
|
|
||||||
"unset border\n"
|
|
||||||
"#set style fill solid 0.1 #pattern 5 (better, but restarts)\n"
|
|
||||||
"plot $Data using 1:($3)*100/%zu with fillsteps lw 2 title \"set\", \\\n"
|
|
||||||
"$Data using 1:($4)*100/%zu with steps lw 1 title \"cumulative\"\n",
|
|
||||||
bible.words.total, bible.words.total);
|
|
||||||
goto finally;
|
|
||||||
catch:
|
|
||||||
success = EXIT_FAILURE;
|
|
||||||
perror("journal");
|
|
||||||
fprintf(stderr, "Details: %s.\n", reason);
|
|
||||||
finally:
|
|
||||||
kjv_(&bible);
|
|
||||||
journal_(&j);
|
|
||||||
return success;
|
|
||||||
}
|
|
14
src/source.h
14
src/source.h
@ -1,12 +1,12 @@
|
|||||||
#if defined BASE \
|
#if defined BASE \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- base */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- base */
|
||||||
#include "pair.h"
|
#include "pair.h"
|
||||||
struct source { struct pair name, desc; };
|
struct source { struct pair name, desc; };
|
||||||
#endif /* base --> */
|
#endif /* base --> */
|
||||||
|
|
||||||
|
|
||||||
#if defined GENERIC \
|
#if defined PRIVATE \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- generic */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- private */
|
||||||
|
|
||||||
struct tree_source_node;
|
struct tree_source_node;
|
||||||
struct tree_source_tree { struct tree_source_node *node; unsigned height; };
|
struct tree_source_tree { struct tree_source_node *node; unsigned height; };
|
||||||
@ -25,11 +25,11 @@ struct sourcemap_table {
|
|||||||
uint32_t log_capacity, size, top;
|
uint32_t log_capacity, size, top;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* generic --> */
|
#endif /* private --> */
|
||||||
|
|
||||||
|
|
||||||
#if defined PROTO \
|
#if defined PROTO \
|
||||||
|| !defined BASE && !defined GENERIC && !defined PROTO /* <!-- proto */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- proto */
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
struct sources {
|
struct sources {
|
||||||
struct sourcelist_array list;
|
struct sourcelist_array list;
|
||||||
@ -46,8 +46,8 @@ const struct source *source_lookup(struct sources *s, const union line64 range);
|
|||||||
#ifdef BASE
|
#ifdef BASE
|
||||||
#undef BASE
|
#undef BASE
|
||||||
#endif
|
#endif
|
||||||
#ifdef GENERIC
|
#ifdef PRIVATE
|
||||||
#undef GENERIC
|
#undef PRIVATE
|
||||||
#endif
|
#endif
|
||||||
#ifdef PROTO
|
#ifdef PROTO
|
||||||
#undef PROTO
|
#undef PROTO
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "../src/kjv.h"
|
#include "../src/kjvcount.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <inttypes.h> /* C99 */
|
#include <inttypes.h> /* C99 */
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -8,19 +8,25 @@
|
|||||||
int main(void) {
|
int main(void) {
|
||||||
int success = EXIT_SUCCESS;
|
int success = EXIT_SUCCESS;
|
||||||
errno = 0; /* `errno` is not set correctly to 0 in some debug situations. */
|
errno = 0; /* `errno` is not set correctly to 0 in some debug situations. */
|
||||||
struct kjv k = kjv();
|
struct kjvcount count = kjvcount();
|
||||||
fprintf(stderr, "%zu total words, %s.\n", k.words.total, kjv_to_string(&k));
|
struct kjvset_table set = kjv_set();
|
||||||
if(kjv_is_empty(&k)) goto catch;
|
fprintf(stderr, "%zu total words, %s.\n",
|
||||||
kjv_add(&k, (union kjvcite){ .book = Genesis, .chapter = 1, .verse = 1 });
|
count.words.total, kjvcount_to_string(&count));
|
||||||
kjv_add(&k, (union kjvcite){ .book = Genesis, .chapter = 1, .verse = 2 });
|
if(kjvcount_is_empty(&count)) goto catch;
|
||||||
kjv_add(&k, (union kjvcite){ .book = Genesis, .chapter = 1, .verse = 1 });
|
kjv_set_add(&set, &count,
|
||||||
fprintf(stderr, "%zu of which: %s.\n", k.words.set, kjv_set_to_string(&k));
|
(union kjvcite){ .book = Genesis, .chapter = 1, .verse = 1 });
|
||||||
assert(k.words.total == 789633);
|
kjv_set_add(&set, &count,
|
||||||
assert(k.words.set == 39);
|
(union kjvcite){ .book = Genesis, .chapter = 1, .verse = 2 });
|
||||||
|
kjv_set_add(&set, &count,
|
||||||
|
(union kjvcite){ .book = Genesis, .chapter = 1, .verse = 1 });
|
||||||
|
fprintf(stderr, "%zu of which: %s.\n", count.words.set, kjv_set_to_string(&set));
|
||||||
|
assert(count.words.total == 789633);
|
||||||
|
assert(count.words.set == 39);
|
||||||
goto finally;
|
goto finally;
|
||||||
catch:
|
catch:
|
||||||
success = EXIT_FAILURE, perror("kjv");
|
success = EXIT_FAILURE, perror("kjv");
|
||||||
finally:
|
finally:
|
||||||
kjv_(&k);
|
kjv_set_(&set);
|
||||||
|
kjvcount_(&count);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user