diff --git a/src/pair.c b/src/pair.c index 14df279..a87a658 100644 --- a/src/pair.c +++ b/src/pair.c @@ -31,6 +31,17 @@ int pair_to_natural(const char *a, const char *const b, uint32_t *const n) { return *n = accum, 1; } +static int pair_to_64(const char *a, const char *const b, uint64_t *const n) { + uint64_t accum = 0; + while(a < b) { + uint64_t next = accum * 10 + (uint64_t)(*a - '0'); + if(accum > next) return errno = ERANGE, 0; + accum = next; + a++; + } + return *n = accum, 1; +} + /** `h0` "1" `h1` ":" `m0` "30" `m1` -> 90 `n` @return Valid. */ int pair_colon_to_minutes(const char *h0, const char *const h1, const char *m0, const char *const m1, uint32_t *const n) { @@ -117,6 +128,27 @@ int pair_to_date(const char *a, union date32 *const d) { return *d = temp, 1; } +int pair_to_cents(const char *a, const char *b, int64_t *const cents) { + uint64_t d, c; + int is_negative; + assert(a && a < b && cents); + if(a[0] == '-') is_negative = 1, a++, assert(a < b); + else is_negative = 0; + if(a + 2 < b && b[-3] == '.') { /* dollars.cents */ + if(!pair_to_64(a, b - 3, &d)) return 0; + c = (uint64_t)(b[-2] - '0') * 10 + (uint64_t)(b[-1] - '0'); + } else { /* dollars */ + if(!pair_to_64(a, b, &d)) return 0; + c = 0; + } + assert(-INT64_MAX >= INT64_MIN); + if(INT64_MAX / 100 < d || INT64_MAX - d * 100 < c) + return errno = ERANGE, 0; + /* Misses one in 2's. Not a very nuanced conversion. */ + *cents = (is_negative ? -1 : 1) * (int64_t)(d * 100 + c); + return 1; +} + /** @return A djb2 hash of `p`. */ uint32_t pair_djb2(struct pair p) { uint32_t hash = 5381, c; diff --git a/src/pair.h b/src/pair.h index 3565357..e7afccd 100644 --- a/src/pair.h +++ b/src/pair.h @@ -15,6 +15,7 @@ int pair_is_equal(struct pair, struct pair); int pair_is_string(struct pair, const char *); #include "journal.h" /* date32 */ int pair_to_date(const char *, union date32 *); +int pair_to_cents(const char *, const char *, int64_t *); uint32_t pair_djb2(struct pair); /* Supporting pair -> size_t for looking up in arrays. */ diff --git a/src/scan.h b/src/scan.h index 8a553f2..0c57a0b 100644 --- a/src/scan.h +++ b/src/scan.h @@ -19,6 +19,24 @@ struct kvpair { struct pair key, value; }; #define TREE_VALUE struct kvpair #define TREE_HEAD #include "../src/tree.h" +enum currency { + NONE, + CAD, /* Canadian $. */ + USD, /* US Dollar $usd. */ + EUR, /* Euro €. */ + GBP, /* Pound £. */ + MXN, /* Mexican pesos $mxn. */ + CUP, /* Cuban pesos, (US) $cup. */ + CUC, /* Cuban convertible $cuc. */ +}; +#include +struct money { int64_t cents; enum currency currency; }; +struct kvmoney { struct pair key; struct money value; }; +#define TREE_NAME linekvmoney +#define TREE_KEY union line64 +#define TREE_VALUE struct kvmoney +#define TREE_HEAD +#include "../src/tree.h" /* Place array. */ struct place { struct pair name; double x, y; }; @@ -116,7 +134,8 @@ struct scan { struct pairmap_table map; struct linemap_tree dates; } edits; - struct linekvpair_tree contacts, books, tvs, movies, ideas; + struct linekvpair_tree contacts, books, tvs, movies, ideas, vaccines; + struct linekvmoney_tree taxes; struct glider_tree gliders; struct flight_tree flights; struct kjv_tree kjvs; diff --git a/src/scan.re.c b/src/scan.re.c index 3718401..9f4fdbd 100644 --- a/src/scan.re.c +++ b/src/scan.re.c @@ -54,6 +54,19 @@ static int linekvpair_compare(const union line64 a, const union line64 b) #define TREE_DEFAULT {{0,0},{0,0}} #define TREE_BODY #include "../src/tree.h" +static void linekvmoney_to_string(const union line64 line, + const struct kvmoney *const kv, + char (*const a)[12]) { (void)kv; date32_to_string(line.date, a); } +static int linekvmoney_compare(const union line64 a, const union line64 b) + { return a.u64 > b.u64; } +#define TREE_NAME linekvmoney +#define TREE_KEY union line64 +#define TREE_VALUE struct kvmoney +#define TREE_COMPARE +#define TREE_TO_STRING +#define TREE_DEFAULT {{0,0},{0,CAD}} +#define TREE_BODY +#include "../src/tree.h" /* Array of places. */ @@ -134,7 +147,8 @@ static int scan_day(struct scan *const scan, union date32 date, size_t line = 1; char datestr[12] = {0}; const char *fail = "perhaps a bat?"; - struct pair *future = 0; + struct pair *input_text = 0; + struct money *input_money = 0; struct score *new_score = 0; struct glider *new_glider = 0; struct flight *new_flight = 0; @@ -171,14 +185,13 @@ static int scan_day(struct scan *const scan, union date32 date, keyword = [A-Za-z0-9][A-Za-z0-9_-]*; - natural = [1-9][0-9]*; - zero_natural = [0-9]+; - decimal = "-"? zero_natural ("." zero_natural?)?; uint = [0-9]+; + natural = [1-9][0-9]*; + decimal = "-"? [0-9]+ ("." [0-9]*)?; date = natural "-" [0-1][0-9] "-" [0-3][0-9]; minutes = [0-5][0-9]; airport = [A-Z0-9]{4,4}; - + moneyamount = "-"? [0-9]+ ("." [0-9][0-9])?; // fixme? kjvlookat = ws* natural ":" natural [ab]? ("-" natural [ab]?)? ws+ "--" ws+; first = ("I" | "1") " "?; second = ("II" | "2") " "?; @@ -207,16 +220,18 @@ static int scan_day(struct scan *const scan, union date32 date, "tv: " :=> tv "movie: " :=> movie "idea: " :=> idea + "vaccine: " :=> vaccine + "tax: " :=> tax * { fail = "document title"; goto catch; } - @s0 bralabel @s1 "]" => future { + @s0 bralabel @s1 "]" => input_text { const union line64 key = { { (uint32_t)line, date } }; size_t *pi; struct kvpair *doc; if(!(doc = kvpair_array_new(&scan->documents.array))) goto catch; doc->key.a = s0, doc->key.b = s1; doc->value.a = 0, doc->value.b = 0; - assert(!future), future = &doc->value; + assert(!input_text), input_text = &doc->value; switch(linemap_tree_bulk_assign(&scan->documents.dates, key, &pi)) { case TREE_PRESENT: fail = "duplicate"; case TREE_ERROR: goto catch; case TREE_ABSENT: @@ -228,14 +243,14 @@ static int scan_day(struct scan *const scan, union date32 date, } * { fail = "remark"; goto catch; } - @s0 date "]" => future { + @s0 date "]" => input_text { const union line64 key = { { (uint32_t)line, date } }; size_t *pi; struct edit *ed; if(!(ed = edit_array_new(&scan->edits.array))) goto catch; ed->edit.u32 = 0; ed->desc.a = 0, ed->desc.b = 0; - assert(!future), future = &ed->desc; + assert(!input_text), input_text = &ed->desc; if(!pair_to_date(s0, &ed->edit)) goto catch; switch(linemap_tree_bulk_assign(&scan->edits.dates, key, &pi)) { case TREE_PRESENT: fail = "duplicate"; case TREE_ERROR: goto catch; @@ -249,7 +264,7 @@ static int scan_day(struct scan *const scan, union date32 date, } * { fail = "contact unrecognized"; goto catch; } - @s0 bralabel @s1 "]" => future { + @s0 bralabel @s1 "]" => input_text { const union line64 key = { { (uint32_t)line, date } }; struct kvpair *pair; switch(linekvpair_tree_bulk_assign(&scan->contacts, key, &pair)) { @@ -258,14 +273,14 @@ static int scan_day(struct scan *const scan, union date32 date, } pair->key.a = s0, pair->key.b = s1; pair->value.a = pair->value.b = 0; - assert(!future), future = &pair->value; + assert(!input_text), input_text = &pair->value; fprintf(stderr, "%s[%zu]: new contact <<%.*s>>.\n", datestr, line, (int)(s1 - s0), s0); continue; } * { fail = "book unrecognized"; goto catch; } - @s0 bralabel @s1 "]" => future { + @s0 bralabel @s1 "]" => input_text { const union line64 key = { { (uint32_t)line, date } }; struct kvpair *pair; switch(linekvpair_tree_bulk_assign(&scan->books, key, &pair)) { @@ -274,14 +289,14 @@ static int scan_day(struct scan *const scan, union date32 date, } pair->key.a = s0, pair->key.b = s1; pair->value.a = pair->value.b = 0; - assert(!future), future = &pair->value; + assert(!input_text), input_text = &pair->value; fprintf(stderr, "%s[%zu]: new book <<%.*s>>.\n", datestr, line, (int)(s1 - s0), s0); continue; } * { fail = "tv unrecognized"; goto catch; } - @s0 bralabel @s1 "]" => future { + @s0 bralabel @s1 "]" => input_text { const union line64 key = { { (uint32_t)line, date } }; struct kvpair *pair; switch(linekvpair_tree_bulk_assign(&scan->tvs, key, &pair)) { @@ -290,14 +305,14 @@ static int scan_day(struct scan *const scan, union date32 date, } pair->key.a = s0, pair->key.b = s1; pair->value.a = pair->value.b = 0; - assert(!future), future = &pair->value; + assert(!input_text), input_text = &pair->value; fprintf(stderr, "%s[%zu]: new tv <<%.*s>>.\n", datestr, line, (int)(s1 - s0), s0); continue; } * { fail = "movie unrecognized"; goto catch; } - @s0 bralabel @s1 "]" => future { + @s0 bralabel @s1 "]" => input_text { const union line64 key = { { (uint32_t)line, date } }; struct kvpair *pair; switch(linekvpair_tree_bulk_assign(&scan->contacts, key, &pair)) { @@ -306,14 +321,14 @@ static int scan_day(struct scan *const scan, union date32 date, } pair->key.a = s0, pair->key.b = s1; pair->value.a = pair->value.b = 0; - assert(!future), future = &pair->value; + assert(!input_text), input_text = &pair->value; fprintf(stderr, "%s[%zu]: new movie <<%.*s>>.\n", datestr, line, (int)(s1 - s0), s0); continue; } * { fail = "idea unrecognized"; goto catch; } - @s0 bralabel @s1 "]" => future { + @s0 bralabel @s1 "]" => input_text { const union line64 key = { { (uint32_t)line, date } }; struct kvpair *pair; switch(linekvpair_tree_bulk_assign(&scan->contacts, key, &pair)) { @@ -322,12 +337,44 @@ static int scan_day(struct scan *const scan, union date32 date, } pair->key.a = s0, pair->key.b = s1; pair->value.a = pair->value.b = 0; - assert(!future), future = &pair->value; + assert(!input_text), input_text = &pair->value; fprintf(stderr, "%s[%zu]: new idea <<%.*s>>.\n", datestr, line, (int)(s1 - s0), s0); continue; } + * { fail = "vaccine unrecognized"; goto catch; } + @s0 bralabel @s1 "]" => input_text { + const union line64 key = { { (uint32_t)line, date } }; + struct kvpair *pair; + switch(linekvpair_tree_bulk_assign(&scan->vaccines, key, &pair)) { + case TREE_PRESENT: fail = "duplicate"; case TREE_ERROR: goto catch; + case TREE_ABSENT: break; + } + pair->key.a = s0, pair->key.b = s1; + pair->value.a = pair->value.b = 0; + assert(!input_text), input_text = &pair->value; + fprintf(stderr, "%s[%zu]: new vaccine <<%.*s>>.\n", + datestr, line, (int)(s1 - s0), s0); + continue; + } + + * { fail = "tax unrecognized"; goto catch; } + @s0 bralabel @s1 "]" => input_money { + const union line64 key = { { (uint32_t)line, date } }; + struct kvmoney *pair; + switch(linekvmoney_tree_bulk_assign(&scan->taxes, key, &pair)) { + case TREE_PRESENT: fail = "duplicate"; case TREE_ERROR: goto catch; + case TREE_ABSENT: break; + } + pair->key.a = s0, pair->key.b = s1; + pair->value = (struct money){0, CAD}; + assert(!input_text), input_money = &pair->value; + fprintf(stderr, "%s[%zu]: new tax <<%.*s>>.\n", + datestr, line, (int)(s1 - s0), s0); + continue; + } + * { fail = "place unrecognized"; goto catch; } @s0 parlabel @s1 / "\n" => skip { also_place: { const struct pair keyword = pair(s0, s1); @@ -397,7 +444,7 @@ static int scan_day(struct scan *const scan, union date32 date, datestr, line, (int)(s1 - s0), s0); } continue; } /* New source. */ - @s0 keyword @s1 ":" => future { + @s0 keyword @s1 ":" => input_text { struct pair keyword = pair(s0, s1); size_t *idx; struct kvpair *source; @@ -410,7 +457,7 @@ static int scan_day(struct scan *const scan, union date32 date, *idx = (size_t)(source - scan->sources.array.data); source->key.a = s0, source->key.b = s1; source->value.a = 0, source->value.b = 0; - assert(!future), future = &source->value; + assert(!input_text), input_text = &source->value; fprintf(stderr, "%s[%zu]: new source <<%.*s>> stored at %zu.\n", datestr, line, (int)(s1 - s0), s0, *idx); goto also_source; @@ -588,28 +635,28 @@ static int scan_day(struct scan *const scan, union date32 date, * { fail = "flight dual time"; goto catch; } ws* ";" => flight_pilot { new_flight->dual_min = 0; continue; } - ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";" + ws* @s0 [0-9]* @s1 "." @t0 [0-9] @t1 "h" ws* ";" => flight_pilot { if(!pair_hours_to_minutes(s0, s1, t0, t1, &new_flight->dual_min)) { fail = "flight dual time"; goto catch; } continue; } * { fail = "flight pilot time"; goto catch; } ws* ";" => flight_ifrsim { new_flight->pilot_min = 0; continue; } - ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";" + ws* @s0 [0-9]* @s1 "." @t0 [0-9] @t1 "h" ws* ";" => flight_ifrsim { if(!pair_hours_to_minutes(s0, s1, t0, t1, &new_flight->pilot_min)) { fail = "flight pilot time"; goto catch; } continue; } * { fail = "flight simulated ifr time"; goto catch; } ws* ";" => flight_ifr { new_flight->ifrsim_min = 0; continue; } - ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";" + ws* @s0 [0-9]* @s1 "." @t0 [0-9] @t1 "h" ws* ";" => flight_ifr { if(!pair_hours_to_minutes(s0, s1, t0, t1, &new_flight->ifrsim_min)) { fail = "flight simulated ifr time"; goto catch; } continue; } * { fail = "flight ifr time"; goto catch; } ws* ";" => flight_remarks { new_flight->ifr_min = 0; continue; } - ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";" + ws* @s0 [0-9]* @s1 "." @t0 [0-9] @t1 "h" ws* ";" => flight_remarks { if(!pair_hours_to_minutes(s0, s1, t0, t1, &new_flight->ifr_min)) { fail = "flight ifr time"; goto catch; } continue; } @@ -737,41 +784,54 @@ static int scan_day(struct scan *const scan, union date32 date, /* fixme: This is a cool way of doing things. Avoid repetition, make all the things this way. */ - /* "<<\ntext\n>>" or "text\n" used by several. Must have pointers to - future input. */ - * { fail = "text input"; goto catch; } - ws+ { continue; } - "\n" => line { // empty is okay + * { fail = "text input"; goto catch; } + ws { continue; } + "\n" => line { // empty is okay line++; - assert(future); - future->a = future->b = 0, future = 0; + fprintf(stderr, "text: \n"); + assert(input_text); + input_text->a = input_text->b = 0, input_text = 0; continue; } - "<<\n" @s0 => future_multi { // multi-line + "<<\n" @s0 => input_text_multi { // multi-line line++; - assert(future); - future->a = s0; + assert(input_text); + input_text->a = s0; continue; } - @s0 anylabel @s1 "\n" => line { // one line + @s0 anylabel @s1 "\n" => line { // one line line++; fprintf(stderr, "text: <<%.*s>>\n", (int)(s1 - s0), s0); - assert(future); - future->a = s0, future->b = s1, future = 0; + assert(input_text); + input_text->a = s0, input_text->b = s1, input_text = 0; continue; } - [^\x00\n] { continue; } - [\x00] { fail = "missing closing \">>\""; goto catch; } - "\n" { line++; continue; } + [^\x00\n] { continue; } + [\x00] + { fail = "missing closing \">>\""; goto catch; } + "\n" { line++; continue; } /* Restricts this to be the last one; you could imagine that it would be more flexible, "<<\n>>; <<\n>>\n". */ - @s1 ">>\n" => line { + @s1 ">>\n" => line { line++; - assert(future && future->a); - future->b = s1, future = 0; + fprintf(stderr, "text: <<\n%.*s>>\n", + (int)(s1 - input_text->a), input_text->a); + assert(input_text && input_text->a); + input_text->b = s1, input_text = 0; continue; } + * + { fail = "money input"; goto catch; } + ws { continue; } + @s0 moneyamount @s1 => input_money_currency { + if(!pair_to_cents(s0, s1, &input_money->cents)) goto catch; + printf("input_money: amount %" PRId64 "\n", input_money->cents); + continue; + } + "$\n" => line + { line++; input_money->currency = CAD; continue; } + */ } assert(0); /* Never gets here. */ catch: @@ -791,6 +851,8 @@ void scan_(struct scan *const scan) { fprintf(stderr, "~scan finished kjv\n"); flight_tree_(&scan->flights); glider_tree_(&scan->gliders); + linekvmoney_tree_(&scan->taxes); + linekvpair_tree_(&scan->vaccines); linekvpair_tree_(&scan->ideas); linekvpair_tree_(&scan->movies); linekvpair_tree_(&scan->tvs); @@ -868,13 +930,14 @@ struct scan scan(struct journal *const jrnl) { || !linekvpair_tree_bulk_finish(&scan.tvs) || !linekvpair_tree_bulk_finish(&scan.movies) || !linekvpair_tree_bulk_finish(&scan.ideas) + || !linekvpair_tree_bulk_finish(&scan.vaccines) + || !linekvmoney_tree_bulk_finish(&scan.taxes) || !glider_tree_bulk_finish(&scan.gliders) || !flight_tree_bulk_finish(&scan.flights) || !kjv_tree_bulk_finish(&scan.kjvs)) goto catch; goto finally; catch: - fprintf(stderr, "Scan failed.\n"); scan_(&scan); finally: return scan;