diff --git a/src/driver.c b/src/driver.c index a6d93fc..4d962ca 100644 --- a/src/driver.c +++ b/src/driver.c @@ -24,10 +24,17 @@ int main(void) { intent = "parse"; scn = scan(&jrnl); - //fprintf(stderr, "Scores: %s.\n", scores_to_string(&scrs)); + //fprintf(stderr, "Scan: %s.\n", scan_to_string(&scrs)); + // <- Not sure what that would do. if(errno) goto catch; - if(!scan_scores_graph(&scn)) goto catch; + intent = "derived/score.gnu"; + if(!freopen(intent, "w", stdout)) goto catch; + scan_score_graph(&scn); + + intent = "derived/glider.gnu"; + if(!freopen(intent, "w", stdout)) goto catch; + scan_glider_graph(&scn); intent = 0; goto finally; diff --git a/src/journal.re.c b/src/journal.re.c index 3a0aa04..3993f78 100644 --- a/src/journal.re.c +++ b/src/journal.re.c @@ -193,7 +193,7 @@ struct journal journal(const char *const dir_journal) { char *contents = 0; sprintf(fn, "%.2d.txt", *d); /* Reconstruct. */ if(!(contents = text_append_file(&j.backing, fn))) goto catch; - switch(day_tree_bulk_try(&j.days, d32, &v.text)) { + switch(day_tree_bulk_assign(&j.days, d32, &v.text)) { case TREE_PRESENT: intent = "not unique", errno = EDOM; /*sic*/ case TREE_ERROR: goto catch; case TREE_ABSENT: break; /* Expected. */ diff --git a/src/scan.h b/src/scan.h index cddc724..c139551 100644 --- a/src/scan.h +++ b/src/scan.h @@ -1,23 +1,21 @@ #include "pair.h" /* pair */ #include "journal.h" /* size_t, date32, line64 */ +/* Date-line to index in array map. */ #define TREE_NAME linemap #define TREE_KEY union line64 #define TREE_VALUE size_t #define TREE_HEAD #include "../src/tree.h" +/* Source array. */ struct source { struct pair name, desc; }; #define ARRAY_NAME source #define ARRAY_TYPE struct source #define ARRAY_HEAD #include "../src/array.h" -struct sources { - struct source_array array; - struct pairmap_table map; - struct linemap_tree dates; -}; +/* Score array. */ struct score { struct pair key, name; union date32 date, last/* update; need to compare leading value */; @@ -27,17 +25,47 @@ struct score { #define ARRAY_TYPE struct score #define ARRAY_HEAD #include "../src/array.h" -struct scores { - struct score_array array; - struct pairmap_table map; - struct linemap_tree dates; + +/* Glider array. */ +#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 +struct glider { + struct pair type, reg, launch, landing; + enum launch_type how; + unsigned height_ft, pilot_min, dual_min, instr_min; + struct pair remarks; }; +#define TREE_NAME glider +#define TREE_KEY union line64 +#define TREE_VALUE struct glider +#define TREE_HEAD +#include "../src/tree.h" + struct scan { - struct sources sources; - struct scores scores; + struct { + struct source_array array; + struct pairmap_table map; + struct linemap_tree dates; + } sources; + struct { + struct score_array array; + struct pairmap_table map; + struct linemap_tree dates; + } scores; + struct glider_tree gliders; }; void scan_(struct scan *); struct scan scan(struct journal *); -int scan_scores_graph(struct scan *); +void scan_score_graph(struct scan *); +void scan_glider_graph(struct scan *); diff --git a/src/scan.re.c b/src/scan.re.c index 3296efb..e071e1f 100644 --- a/src/scan.re.c +++ b/src/scan.re.c @@ -52,6 +52,18 @@ static void score_to_string(const struct score *const s, #define ARRAY_BODY #include "../src/array.h" +/* Glider tree. */ +static void glider_to_string(const union line64 line, const struct glider *g, + char (*const a)[12]) { (void)g; date32_to_string(line.date, a); } +static int glider_compare(const union line64 a, const union line64 b) + { return a.u64 > b.u64; } +#define TREE_NAME glider +#define TREE_KEY union line64 +#define TREE_VALUE struct glider +#define TREE_COMPARE +#define TREE_TO_STRING +#define TREE_BODY +#include "../src/tree.h" @@ -59,12 +71,13 @@ static void score_to_string(const struct score *const s, static int scan_day(struct scan *const scan, union date32 date, const char *const buffer) { - const char *YYCURSOR, *YYMARKER, *yyt1, *yyt2, *s0, *s1; + const char *YYCURSOR, *YYMARKER, *yyt1, *yyt2, *s0, *s1, *t0, *t1; enum YYCONDTYPE condition = yycline; size_t line = 1; char datestr[12] = {0}; const char *fail = "perhaps a bat?"; - struct score *score = 0; + struct score *new_score = 0; + struct glider *new_glider = 0; assert(scan && date.u32 && buffer); YYCURSOR = YYMARKER = yyt1 = buffer; /*!re2c /**/ @@ -79,20 +92,23 @@ static int scan_day(struct scan *const scan, union date32 date, glyph = [^\x00-\x20\x7f]; // [^\x00\n\t ] + all weird semitext = glyph \ ";"; natural = [1-9][0-9]*; + zero_natural = natural | "0"; uint = [0-9]+; keyword = [A-Za-z0-9][A-Za-z0-9_-]*; date = natural "-" [0-1][0-9] "-" [0-3][0-9]; + minutes = [0-5][0-9]; + airport = [A-Z0-9]{4,4}; */ for( ; ; ) { /*!re2c /**/ - /* Default ignore. */ - [^\n\x00] { continue; } + [^\n\x00] { continue; } /* Default ignore. */ "\x00" { fail = "no newline at end of file"; goto catch; } "\x00" { return 1; } /* End of day. */ "\n" => line { line++; continue; } * :=> skip "--" / [^-] :=> source "::" / [^:] :=> score + "[glider]" :=> glider_type * { fail = "source unrecognized"; goto catch; } @@ -104,16 +120,14 @@ static int scan_day(struct scan *const scan, union date32 date, { errno = ERANGE; fail = "too many lines of text"; goto catch; } if(!(i = pair_map_table_get(&scan->sources.map, keyword))) { fail = "keyword not introduced"; goto catch; } - switch(linemap_tree_try(&scan->sources.dates, key, &pi)) { - case TREE_PRESENT: fail = "duplicate key"; /* _Sic_. */ - case TREE_ERROR: goto catch; + switch(linemap_tree_bulk_assign(&scan->sources.dates, key, &pi)) { + case TREE_PRESENT: fail = "duplicate"; case TREE_ERROR: goto catch; case TREE_ABSENT: *pi = i; break; } date32_to_string(date, &datestr); fprintf(stderr, "%s: source <<%.*s>>\n", datestr, (int)(s1 - s0), s0); - continue; - } } + } continue; } /* New source. fixme: desc not set. */ @s0 keyword @s1 ":" [^\x00\n]+ / "\n" => skip { struct pair keyword = pair(s0, s1); @@ -138,7 +152,7 @@ static int scan_day(struct scan *const scan, union date32 date, * { fail = "score unrecognized"; goto catch; } /* Already there. Use the map to get the index from the keyword and then stick a marker in the tree with that index. */ - @s0 keyword @s1 / "\n" => skip { also_score: { + @s0 keyword @s1 / "\n" => skip { new_score: { const struct pair keyword = pair(s0, s1); const union line64 key = { { (uint32_t)line, date } }; size_t idx, *pidx; @@ -149,61 +163,129 @@ static int scan_day(struct scan *const scan, union date32 date, if(scan->scores.array.data[idx].last.u32 >= date.u32) { fail = "duplicate key in same day"; goto catch; } scan->scores.array.data[idx].last.u32 = date.u32; - switch(linemap_tree_bulk_try(&scan->scores.dates, key, &pidx)) { + switch(linemap_tree_bulk_assign(&scan->scores.dates, key, &pidx)) { case TREE_PRESENT: assert(0); fail = "duplicate key"; /* _Sic_. */ case TREE_ERROR: goto catch; case TREE_ABSENT: *pidx = idx; break; } date32_to_string(date, &datestr); fprintf(stderr, "%s: score <%.*s>\n", datestr, (int)(s1 - s0), s0); - continue; - } } + } continue; } /* New score. */ @s0 keyword @s1 ":" => score_name { size_t *idx; + assert(!new_score); + /* Create a new mapping from dateline to scores array. */ switch(pair_map_table_assign(&scan->scores.map, pair(s0, s1), &idx)) { case TABLE_PRESENT: errno = EDOM; fail = "new keyword already used"; case TABLE_ERROR: goto catch; /* _Sic_. */ case TABLE_ABSENT: *idx = 0; break; } - if(!(score = score_array_new(&scan->scores.array))) goto catch; - *idx = (size_t)(score - scan->scores.array.data); /* Offset. */ + /* New entry in the scores array for this map to point to. */ + if(!(new_score = score_array_new(&scan->scores.array))) goto catch; + *idx = (size_t)(new_score - scan->scores.array.data); /* Offset. */ /*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->score = 0; + new_score->key.a = s0, new_score->key.b = s1; + new_score->name.a = 0, new_score->name.b = 0; + new_score->date.u32 = new_score->last.u32 = 0; + new_score->edges = 0, new_score->score = 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_score; + goto new_score; } * { fail = "name unrecognized"; goto catch; } - * { fail = "date unrecognized"; goto catch; } - * { fail = "edges unrecognized"; goto catch; } ws* @s0 semitext+ (" " semitext+)* @s1 /* ws* */ ";" => score_date { - assert(score); - score->name.a = s0, score->name.b = s1; + assert(new_score); + new_score->name.a = s0, new_score->name.b = s1; } + * { fail = "date unrecognized"; goto catch; } ws* "~"? @s0 date ws* ";" => score_edges { - assert(score); - if(!pair_to_date(s0, &score->date)) goto catch; + assert(new_score); + if(!pair_to_date(s0, &new_score->date)) goto catch; } + * { fail = "edges unrecognized"; goto catch; } ws* "~"? @s0 uint @s1 ws* / "\n" => skip { - assert(score); - if(!pair_to_natural(s0, s1, &score->edges)) goto catch; - score = 0; /* Done. */ + assert(new_score); + if(!pair_to_natural(s0, s1, &new_score->edges)) goto catch; + new_score = 0; /* Done. */ + continue; } + /* type, reg, launch, how, height, landing, pilot, dual, instr, remarks + eg, [glider] 2-33A; C-GCLK; CYQQ; A; 2000'; CYQQ; ;:13;; Peters D1 */ + * { fail = "glider type"; goto catch; } + ws* @s0 semitext+ @s1 ws* ";" => glider_reg { + const union line64 key + = {{ (uint32_t)line, {{ date.day, date.month, date.year }} }}; + assert(!new_glider); + if(line > UINT32_MAX) { fail = "line overflow"; goto catch; } + switch(glider_tree_bulk_assign(&scan->gliders, key, &new_glider)) { + case TREE_PRESENT: fail = "duplicate"; case TREE_ERROR: goto catch; + case TREE_ABSENT: break; + } + new_glider->type.a = s0, new_glider->type.b = s1; + continue; + } + * { fail = "glider reg"; goto catch; } + ws* @s0 semitext+ @s1 ws* ";" => glider_launch + { new_glider->reg.a = s0, new_glider->reg.b = s1; continue; } + * { fail = "glider launch"; goto catch; } + ws* @s0 airport @s1 ws* ";" => glider_how + { new_glider->launch.a = s0, new_glider->launch.b = s1; continue; } + * { fail = "glider how"; goto catch; } + ws* @s0 [MWA] ws* ";" => glider_height { + switch(*s0) { + case 'M': new_glider->how = MotorCarTow; break; + case 'W': new_glider->how = Winch; break; + case 'A': new_glider->how = AeroTow; break; + } + continue; + } + * { fail = "glider height"; goto catch; } + ws* @s0 natural @s1 "'" ws* ";" => glider_landing + { if(!pair_to_natural(s0, s1, &new_glider->height_ft)); continue; } + * { fail = "glider landing"; goto catch; } + ws* @s0 airport @s1 ws* ";" => glider_pilot + { new_glider->landing.a = s0, new_glider->landing.b = s1; continue; } + * { fail = "glider pilot"; goto catch; } + ws* ";" => glider_dual /* not PIC */ + { new_glider->pilot_min = 0; continue; } + ws* @s0 natural? @s1 ":" @t0 minutes @t1 ws* ";" + => glider_dual { if(!pair_colon_to_minutes(s0, s1, t0, t1, + &new_glider->pilot_min)) { fail = "pilot time"; goto catch; } + continue; } + * { fail = "glider dual"; goto catch; } + ws* ";" => glider_instr + { new_glider->dual_min = 0; continue; } + ws* @s0 natural? @s1 ":" @t0 minutes @t1 ws* ";" + => glider_instr { if(!pair_colon_to_minutes(s0, s1, t0, t1, + &new_glider->dual_min)) { fail = "dual time"; goto catch; } + continue; } + * { fail = "glider instr"; goto catch; } + ws* ";" => glider_remarks + { new_glider->instr_min = 0; continue; } + ws* @s0 natural? @s1 ":" @t0 minutes @t1 ws* ";" + => glider_remarks { if(!pair_hours_to_minutes(s0, s1, t0, t1, + &new_glider->instr_min)) { fail = "instr time"; goto catch; } + continue; } + * { fail = "glider remarks"; goto catch; } + ws* "\n" => line + { new_glider->remarks.a = new_glider->remarks.b = 0; + new_glider = 0; line++; continue; } + ws* @s0 glyph+ (" " glyph+)* @s1 "\n" => line + { new_glider->remarks.a = s0, new_glider->remarks.b = s1; + new_glider = 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, fail); + fprintf(stderr, "%s line %zu: %s condition %d.\n", datestr, line, fail, condition); return 0; } @@ -238,6 +320,11 @@ struct scan scan(struct journal *const jrnl) { it = journal_iterator(jrnl); while(journal_next(&it, &date, &text)) if(!scan_day(&scan, date, text)) goto catch; + + /* Scans make trees bulk-loaded; fix to real tree. */ + if(!linemap_tree_bulk_finish(&scan.sources.dates) + || !linemap_tree_bulk_finish(&scan.scores.dates)) goto catch; + fprintf(stderr, "List of scores: %s.\n" "Mapped to indices: %s.\n" "Date-line tree: %s.\n", @@ -251,14 +338,30 @@ finally: return scan; } -int scan_scores_graph(struct scan *const scan) { - struct scores *const scrs = &scan->scores; - struct linemap_tree_iterator it = linemap_tree_iterator(&scrs->dates); +/** Lookup the last source in `range` in sources `s`. They are invalidated on + adding a source, (currently fine because we get all at once.) */ +static const struct source *source_lookup(struct scan *const scan, + const union line64 x) { + struct linemap_tree_iterator it; + assert(scan); + it = linemap_tree_less(&scan->sources.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 `array[0]`. */ + return scan->sources.array.data + (linemap_tree_has_element(&it) + && x.date.u32 == linemap_tree_key(&it).date.u32 + ? *linemap_tree_value(&it) : 0); +} + +void scan_score_graph(struct scan *const scan) { + struct linemap_tree_iterator it + = linemap_tree_iterator(&scan->scores.dates); union line64 line; struct score *score; + assert(scan); /* Set score to zero to verify count with paper journal. */ - for(size_t i = 0; i < scrs->array.size; i++) scrs->array.data[i].score = 0; + for(struct score *i = scan->scores.array.data, + *const z = i + scan->scores.array.size; i < z; i++) i->score = 0; /* 840 with legend; only useful to me. */ printf("set terminal pngcairo dashed transparent truecolor" @@ -268,7 +371,8 @@ int scan_scores_graph(struct scan *const scan) { "# date, key, key score\n"); while(linemap_tree_next(&it)) { line = linemap_tree_key(&it); - score = scrs->array.data + *linemap_tree_value(&it); + assert(*linemap_tree_value(&it) < scan->scores.array.size); + score = scan->scores.array.data + *linemap_tree_value(&it); char datestr[12]; date32_to_string(line.date, &datestr); score->score++; @@ -317,5 +421,77 @@ int scan_scores_graph(struct scan *const scan) { " (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 + +void scan_glider_graph(struct scan *const scan) { + assert(scan); + fprintf(stderr, "Glider: %s.\n", glider_tree_to_string(&scan->gliders)); + printf("set terminal pngcairo dashed transparent truecolor" + " size 840, 480 fontscale 1\n" + "set output \"glider.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 <gliders); + while(glider_tree_next(&it)) { + const union line64 line = glider_tree_key(&it); + const struct glider *glider = glider_tree_value(&it); + char datestr[12]; + date32_to_string(line.date, &datestr); + const struct source *src = source_lookup(scan, line); + assert(src); + if(!src->name.a) { fprintf(stderr, "Glider has no source at %s.\n", + datestr); continue; } + printf("%s, ", datestr); + printf("%.*s, %" PRIu32 ", %" PRIu32, + (int)(glider->reg.b - glider->reg.a), glider->reg.a, + glider->dual_min, glider->pilot_min + glider->instr_min); + /* 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"*/); } diff --git a/src/table.h b/src/table.h index 0cd6909..beaefda 100644 --- a/src/table.h +++ b/src/table.h @@ -632,8 +632,8 @@ static int N_(table_contains)(struct N_(table) *const table, const PN_(key) key) } #ifdef TABLE_VALUE /* */ @@ -1565,11 +1567,11 @@ static void PB_(unused_base)(void) { B_(tree_less_or)(0, k, k); B_(tree_more_or)(0, k, k); B_(tree_next)(0); B_(tree_has_element)(0); #ifdef TREE_VALUE - B_(tree_bulk_try)(0, k, 0); B_(tree_try)(0, k, 0); - B_(tree_assign)(0, k, 0, 0); B_(tree_value)(0); + B_(tree_bulk_assign)(0, k, 0); B_(tree_assign)(0, k, 0); + B_(tree_update)(0, k, 0, 0); B_(tree_value)(0); #else B_(tree_bulk_try)(0, k); B_(tree_try)(0, k); - B_(tree_assign)(0, k, 0); + B_(tree_update)(0, k, 0); #endif B_(tree_bulk_finish)(0); B_(tree_remove)(0, k); B_(tree_clone)(0, 0); B_(tree_iterator)(0); B_(tree_less)(0, k); B_(tree_more)(0, k);