diff --git a/Makefile b/Makefile index 29cbad9..768f5bc 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ else CF += -g endif -projects := bin/test-text bin/test-kjv +projects := bin/test-text bin/test-kjv bin/test-journal #docs := $(patsubst test/test_%.c, doc/%.md, $(wildcard test/test_*.c)) default: $(projects) @@ -37,7 +37,8 @@ default: $(projects) bin/test-text: build/test_text.o build/text.o bin/test-kjv: build/test_kjv.o build/text.o build/kjv.o -bin/test-journal: build/test_journal.o +bin/test-journal: build/test_journal.o build/text.o build/journal.o +# and h bin/%: # linking test $@ @@ -67,7 +68,7 @@ build/%.c: src/%.re.c # # https://github.com/neil-edelman/cdoc documentation # -cdoc -o $@ $< -.SECONDARY: build/kjv.c +.SECONDARY: build/kjv.c build/journal.c .PHONY: clean release test test: $(projects) diff --git a/src/char_array_struct.h b/src/char_array_struct.h deleted file mode 100644 index 50f4af6..0000000 --- a/src/char_array_struct.h +++ /dev/null @@ -1,4 +0,0 @@ -/* This is a duplicate for publishing. */ - -#include -struct char_array { char *data; size_t size, capacity; }; diff --git a/src/journal.h b/src/journal.h index 8b13789..8b52935 100644 --- a/src/journal.h +++ b/src/journal.h @@ -1 +1,31 @@ +#ifndef OMIT_BASE /* */ + +#ifndef OMIT_DAY /* */ + + +#ifndef OMIT_PROTO /* */ diff --git a/src/journal.re.c b/src/journal.re.c index 2254987..390b623 100644 --- a/src/journal.re.c +++ b/src/journal.re.c @@ -1,27 +1,29 @@ -#include "../src/journal.h" #include "../src/text.h" +#define OMIT_DAY +#define OMIT_PROTO +#include "../src/journal.h" /* base */ #include /* C99 */ -#include #include +#include #include +#include +#include /* chdir (POSIX) */ +#include /* mode_t (POSIX) */ +#include /* umask (POSIX) */ +#include /* opendir readdir closedir */ -/** 1. reverse ordering, 2. uint32_t is a valid union type, - 3. C11 or GNU anonymous unions. */ -union date32 { - struct { uint32_t day : 5, month : 4, year : 23; }; - uint32_t u32; -}; -static void date32_to_string(const union date32 d, char (*const z)[12]) { - assert(d.year < 10000 && d.month && d.month <= 31 && d.day && d.day <= 31); - sprintf(*z, "%u-%2.2u-%2.2u", d.year % 10000, d.month, d.day); + +static void date32_to_string(const union date32 d, char (*const a)[12]) { + assert(a + && d.year < 10000 && d.month && d.month <= 31 && d.day && d.day <= 31); + sprintf(*a, "%" PRIu32 "-%2.2" PRIu32 "-%2.2" PRIu32, + (unsigned)d.year % 10000, (unsigned)d.month, (unsigned)d.day); } -static int page_compare(const union date32 a, const union date32 b) +static int day_compare(const union date32 a, const union date32 b) { return a.u32 > b.u32; } -static void page_to_string(const union date32 d, char (*const z)[12]) - { date32_to_string(d, z); } -static void page_to_string(const struct page_tree_entry entry, - char (*const z)[12]) { date32_to_string(*entry.key, z); } -#define TREE_NAME page +static void day_to_string(const union date32 d, char *const*const entry, + char (*const a)[12]) { (void)entry; date32_to_string(d, a); } +#define TREE_NAME day #define TREE_KEY union date32 #define TREE_VALUE char * #define TREE_COMPARE @@ -85,4 +87,245 @@ static unsigned looks_like_day(const char *const a) { */ } +#if 0 +/** Is `y` a leap-year? */ +static int leap(int y) { + assert(y >= 1582); + if(!(y % 400)) return 1; + if(!(y % 100)) return 0; + if(!(y % 4)) return 1; + return 0; +} +/** @return Pack into `date32` or return zero. */ +static union date32 date_to_32(const int y, const int m, const int d) { + union date32 d32 = { 0 }; + /* Leap year calculations only work at y>=1 and Gregorian Calendar and max + 23 bits. */ + if(y < 1582 || y > 8388607 || m < 1 || m > 12 || d < 1 || d > 31) goto no; + switch(m) { + case 1: case 3: case 5: case 7: case 8: case 10: case 12: break; + case 4: case 6: case 9: case 11: if(d > 30) goto no; break; + case 2: if(d > 28 + leap(y)) goto no; break; + default: assert(0); break; + } + d32.year = (unsigned)y, d32.month = (unsigned)m, d32.day = (unsigned)d; +no: + return d32; +} +/** Tomohiko Sakamoto comp.lang.c 1993-04-10. */ +static unsigned weekday(union date32 d) { + d.year -= d.month < 3; + return (d.year + d.year / 4 - d.year / 100 + d.year / 400 + + "-bed=pen+mad."[d.month] + d.day) % 7; +} +#endif + +#define OMIT_BASE +#define OMIT_DAY +#include "../src/journal.h" /* Just prototypes. */ + +void journal_(struct journal *const j) { +} + +struct journal journal(void) { + struct journal j = {0}; + return j; +} + +int journal_is_valid(const struct journal *const j) { + return 0; +} + +const char *journal_to_string(const struct journal *const j) + { return day_tree_to_string(&j->days); } + + + +#if 0 + +int main(int argc, char **argv) { + int success = EXIT_SUCCESS; + char *intent = 0; + + /* For reading in files, overwritten. */ + DIR *dir = 0; + struct dirent *de; + struct int_array years = int_array(), months = int_array(), + days = int_array(); + int *y, *y_end; + + struct page_tree journal = page_tree(); + + errno = 0; + if(argc != 2) { intent = "needs journal location, which should" + " contain //.txt"; goto catch; } + + /* Get the years list as directories matching a year. */ + if(chdir(argv[1]) == -1 || !(dir = opendir("."))) goto catch; + while((de = readdir(dir))) { + struct stat st; + int year, *p; + if(!lex_looks_like_year(de->d_name, &year)) continue; + if(stat(de->d_name, &st)) goto catch; + if(!S_ISDIR(st.st_mode)) continue; + if(!(p = int_array_new(&years))) goto catch; + *p = year; + } + closedir(dir), dir = 0; + /* Sort the years for sensible ordering of parsing. */ + qsort(years.data, years.size, sizeof *years.data, &void_int_cmp); + fprintf(stderr, "Years in <<%s>>: %s.\n", + argv[1], int_array_to_string(&years)); + + /* Go though each year. */ + for(y = years.data, y_end = y + years.size; y < y_end; y++) { + char fn[64]; + int *m, *m_end; + sprintf(fn, "%d", *y); + + /* Get the months as directories. */ + if(chdir(fn) == -1 || !(dir = opendir("."))) goto catch; + while((de = readdir(dir))) { + struct stat st; + int month, *p; + if(!(month = lex_looks_like_month(de->d_name))) continue; + if(stat(de->d_name, &st)) goto catch; + if(!S_ISDIR(st.st_mode)) continue; + if(!(p = int_array_new(&months))) goto catch; + *p = month; + } + closedir(dir), dir = 0; + qsort(months.data, months.size, sizeof *months.data, &void_int_cmp); + fprintf(stderr, "Months in <<%s>>: %s.)\n", + fn, int_array_to_string(&months)); + + /* Go though each month. */ + for(m = months.data, m_end = m + months.size; m < m_end; m++) { + int *d, *d_end; + sprintf(fn, "%.2d", *m); + + /* Get the days as files. */ + if(chdir(fn) == -1 || !(dir = opendir("."))) goto catch; + while((de = readdir(dir))) { + struct stat st; + int day, *p; + /* fixme: Have yyyy-mm-dd to figure out how many days. */ + if(!(day = lex_looks_like_day(de->d_name))) continue; + if(stat(de->d_name, &st)) goto catch; + if(S_ISDIR(st.st_mode)) continue; + if(!(p = int_array_new(&days))) goto catch; + *p = day; + } + closedir(dir), dir = 0; + qsort(days.data, days.size, sizeof *days.data, &void_int_cmp); + fprintf(stderr, "Days in <<%s>>: %s.\n", + fn, int_array_to_string(&days)); + + for(d = days.data, d_end = d + days.size; d < d_end; d++) { + struct lex *lex = 0; + struct page *page = 0; + union date32 d32; + if(!(d32 = date_to_32(*y, *m, *d)).year) { errno = EILSEQ; + intent = "date parse error"; goto syntax; } + sprintf(fn, "%.2d.txt", *d); + if(page_tree_bulk_add(&journal, d32, &page) != TREE_UNIQUE) { + if(!errno) intent = "not unique", errno = EDOM; + goto syntax; + } + page->entry = char_array(); + page->meaning = lex_array(); + if(!append_file(&page->entry, fn)) goto syntax; + int first = 1; + for(lex_reset(page->entry.data); ; ) { + if(!(lex = lex_array_new(&page->meaning))) goto syntax; + if(!lex_next(lex)) { + if(lex->symbol != END) { errno = EILSEQ; goto syntax; } + break; /* Terminated successfully. */ + } + switch(lex->symbol) { + case TEXT: printf("%s%.*s", + first ? "" : " ", (int)(lex->s1 - lex->s0), lex->s0); + first = 0; break; + case PARAGRAPH: printf("\n" C_RESET); break; + case KJV_BOOK: printf(C_YELLOW "%.*s", + (int)(lex->s1 - lex->s0), lex->s0); break; + case KJV_CHAPTER_VERSE: printf(" ch. %.*s", + (int)(lex->s1 - lex->s0), lex->s0); break; + case KJV_TEXT: printf("%.*s", + (int)(lex->s1 - lex->s0), lex->s0); break; + case KJV_NEXT: printf("(next)\n"); break; + default: + fprintf(stderr, "%lu: %s", + (unsigned long)lex->line, lex_symbols[lex->symbol]); + if(lex->s0 && lex->s1) { + if(lex->s0 + INT_MAX < lex->s1) + intent = "line too long", errno = EILSEQ; + else + fprintf(stderr, " <<%.*s>>", + (int)(lex->s1 - lex->s0), lex->s0); + } + fprintf(stderr, ".\n"); + break; + } + } + continue; +syntax: + fprintf(stderr, "On date: %d-%.2d-%.2d.\n", *y, *m, *d); + if(!page) goto catch; + if(!lex) { fprintf(stderr, "While parsing <<<\n%s>>>.\n", + page->entry.data); goto catch; } + for(struct lex_array_iterator it + = lex_array_iterator(&page->meaning); + lex = lex_array_next(&it); ) { + fprintf(stderr, "%lu: %s", + (unsigned long)lex->line, lex_symbols[lex->symbol]); + if(lex->s0 && lex->s1) { + if(lex->s0 + INT_MAX < lex->s1) + intent = "line too long", errno = EILSEQ; + else + fprintf(stderr, " <<%.*s>>", + (int)(lex->s1 - lex->s0), lex->s0); + } + fprintf(stderr, ".\n"); + } + goto catch; + } + + int_array_clear(&days); + if(chdir("..") == -1) goto catch; + } + + int_array_clear(&months); + if(chdir("..") == -1) goto catch; + /* fixme: Expand, contact is the next thing that it doesn't get. */ + if(*y == 1993/*1996*/) break; + } + page_tree_bulk_finish(&journal); + int_array_(&years), int_array_(&months), int_array_(&days); + fprintf(stderr, "Journal has entries: %s\n", page_tree_to_string(&journal)); + + /* Do something interesting? */ + if(!bible_graph(&journal)) goto catch; + + goto finally; +catch: + success = EXIT_FAILURE; + perror("interpret"); + if(intent) fprintf(stderr, "Further explanation: %s.\n", intent); +finally: + if(dir && closedir(dir)) success = EXIT_FAILURE, perror("dir"); + int_array_(&years), int_array_(&months), int_array_(&days); + struct page_tree_entry entry; + for(struct page_tree_iterator it = page_tree_begin(&journal); + (entry = page_tree_next(&it)).key; ) { + struct page *const page = entry.value; + char z[12]; + date32_to_string(*entry.key, &z); + lex_array_(&page->meaning); + char_array_(&page->entry); + } + return success; +} + +#endif diff --git a/src/kjv.h b/src/kjv.h index 399629c..0326345 100644 --- a/src/kjv.h +++ b/src/kjv.h @@ -1,4 +1,4 @@ -#ifndef KJV_OMIT_BASE /* */ -#ifndef KJV_OMIT_VERSES /* */ -#ifndef KJV_OMIT_PROTO /* */ diff --git a/src/kjv.re.c b/src/kjv.re.c index da03523..848fe38 100644 --- a/src/kjv.re.c +++ b/src/kjv.re.c @@ -8,9 +8,10 @@ @std C11 */ #include "../src/text.h" -#define KJV_OMIT_VERSES -#define KJV_OMIT_PROTO +#define OMIT_VERSES +#define OMIT_PROTO #include "../src/kjv.h" /* Just the base data. */ +#include #include #include #include @@ -45,7 +46,7 @@ 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]) - { sprintf(*a, "%.4s%u:%u", kjv_book_string[x.book], + { sprintf(*a, "%.4s%" PRIu32 ":%" PRIu32, kjv_book_string[x.book], x.chapter % 1000, x.verse % 1000); } #define TABLE_NAME kjvset #define TABLE_KEY union kjvcite @@ -153,8 +154,8 @@ scan: } -#define KJV_OMIT_BASE -#define KJV_OMIT_VERSES +#define OMIT_BASE +#define OMIT_VERSES #include "../src/kjv.h" /* Just the kjv and prototypes. */ /** Frees `kjv`. */ @@ -169,7 +170,7 @@ void kjv_(struct kjv *const kjv) { to tell. */ struct kjv kjv(void) { const char *const dir_kjv = "kjv"; - struct text backing; + struct text backing = text(); struct kjv kjv = { 0 }; DIR *dir = 0; struct dirent *de = 0; diff --git a/test/test_journal.c b/test/test_journal.c index 86aa580..84ad08e 100644 --- a/test/test_journal.c +++ b/test/test_journal.c @@ -8,3 +8,58 @@ int main(void) { printf("Hello, World!\n"); return 0; } + +#if 0 +/* +### plot with steps +reset session + +$Data <