currency started

This commit is contained in:
Neil 2023-05-08 15:43:16 -07:00
parent 2d11f84bd9
commit 3e5acec91a
4 changed files with 161 additions and 46 deletions

View File

@ -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 <http://www.cse.yorku.ca/~oz/hash.html> hash of `p`. */
uint32_t pair_djb2(struct pair p) {
uint32_t hash = 5381, c;

View File

@ -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. */

View File

@ -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 <stdint.h>
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;

View File

@ -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,
<bracket> "tv: " :=> tv
<bracket> "movie: " :=> movie
<bracket> "idea: " :=> idea
<bracket> "vaccine: " :=> vaccine
<bracket> "tax: " :=> tax
<document> * { fail = "document title"; goto catch; }
<document> @s0 bralabel @s1 "]" => future {
<document> @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,
}
<remark> * { fail = "remark"; goto catch; }
<remark> @s0 date "]" => future {
<remark> @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,
}
<contact> * { fail = "contact unrecognized"; goto catch; }
<contact> @s0 bralabel @s1 "]" => future {
<contact> @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;
}
<book> * { fail = "book unrecognized"; goto catch; }
<book> @s0 bralabel @s1 "]" => future {
<book> @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;
}
<tv> * { fail = "tv unrecognized"; goto catch; }
<tv> @s0 bralabel @s1 "]" => future {
<tv> @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;
}
<movie> * { fail = "movie unrecognized"; goto catch; }
<movie> @s0 bralabel @s1 "]" => future {
<movie> @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;
}
<idea> * { fail = "idea unrecognized"; goto catch; }
<idea> @s0 bralabel @s1 "]" => future {
<idea> @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;
}
<vaccine> * { fail = "vaccine unrecognized"; goto catch; }
<vaccine> @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;
}
<tax> * { fail = "tax unrecognized"; goto catch; }
<tax> @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;
}
<place> * { fail = "place unrecognized"; goto catch; }
<place> @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. */
<source> @s0 keyword @s1 ":" => future {
<source> @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,
<flight_dual> * { fail = "flight dual time"; goto catch; }
<flight_dual> ws* ";" => flight_pilot
{ new_flight->dual_min = 0; continue; }
<flight_dual> ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";"
<flight_dual> 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; }
<flight_pilot> * { fail = "flight pilot time"; goto catch; }
<flight_pilot> ws* ";" => flight_ifrsim
{ new_flight->pilot_min = 0; continue; }
<flight_pilot> ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";"
<flight_pilot> 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; }
<flight_ifrsim> * { fail = "flight simulated ifr time"; goto catch; }
<flight_ifrsim> ws* ";" => flight_ifr
{ new_flight->ifrsim_min = 0; continue; }
<flight_ifrsim> ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";"
<flight_ifrsim> 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; }
<flight_ifr> * { fail = "flight ifr time"; goto catch; }
<flight_ifr> ws* ";" => flight_remarks
{ new_flight->ifr_min = 0; continue; }
<flight_ifr> ws* @s0 zero_natural? @s1 "." @t0 [0-9] @t1 "h" ws* ";"
<flight_ifr> 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. */
<future, future_multi> * { fail = "text input"; goto catch; }
<future> ws+ { continue; }
<future> "\n" => line { // empty is okay
<input_text, input_text_multi> * { fail = "text input"; goto catch; }
<input_text> ws { continue; }
<input_text> "\n" => line { // empty is okay
line++;
assert(future);
future->a = future->b = 0, future = 0;
fprintf(stderr, "text: <empty>\n");
assert(input_text);
input_text->a = input_text->b = 0, input_text = 0;
continue;
}
<future> "<<\n" @s0 => future_multi { // multi-line
<input_text> "<<\n" @s0 => input_text_multi { // multi-line
line++;
assert(future);
future->a = s0;
assert(input_text);
input_text->a = s0;
continue;
}
<future> @s0 anylabel @s1 "\n" => line { // one line
<input_text> @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;
}
<future_multi> [^\x00\n] { continue; }
<future_multi> [\x00] { fail = "missing closing \">>\""; goto catch; }
<future_multi> "\n" { line++; continue; }
<input_text_multi> [^\x00\n] { continue; }
<input_text_multi> [\x00]
{ fail = "missing closing \">>\""; goto catch; }
<input_text_multi> "\n" { line++; continue; }
/* Restricts this to be the last one; you could imagine that it would
be more flexible, "<<\n>>; <<\n>>\n". */
<future_multi> @s1 ">>\n" => line {
<input_text_multi> @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;
}
<input_money, input_money_currency> *
{ fail = "money input"; goto catch; }
<input_money> ws { continue; }
<input_money> @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;
}
<input_money_currency> "$\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;