Add an RPN date manipulator

This commit is contained in:
Laurence Withers 2007-07-29 11:53:55 +00:00
parent 124df16711
commit e59586b265
2 changed files with 305 additions and 1 deletions

View File

@ -25,9 +25,14 @@ then
fi
done
case "${TEST}" in
obj/tests/manip) TEST_EXTRAS="-lm" ;;
*) TEST_EXTRAS="" ;;
esac
if [ ${MODIFIED} -ne 0 ]
then
do_cmd ${CC} -Iobj ${CFLAGS} -o ${TEST} ${SRC} ${LIBS} ${EXTRAS} || return 1
do_cmd ${CC} -Iobj ${CFLAGS} -o ${TEST} ${SRC} ${LIBS} ${EXTRAS} ${TEST_EXTRAS} || return 1
print_success "Built ${TEST}"
else
print_success "${TEST} is up to date"

299
src/tests/manip.c Normal file
View File

@ -0,0 +1,299 @@
/* libiso8601/src/tests/manip.c
*
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
* Released under the GNU GPLv2. See file COPYING or
* http://www.gnu.org/copyleft/gpl.html for details.
*/
#include "iso8601.h"
#include <math.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* safe_strtod()
* Converts `str' into a double, which it will store in `out'. If an error occurs, returns -1,
* else 0 on success.
*/
int safe_strtod(const char* str, double* out)
{
char* endp = 0;
errno = 0;
*out = strtod(str, &endp);
if(errno || !endp || *endp) return -1;
return 0;
}
/* date_stack
* We use a stack of dates and/or periods to implement effectively an RPN manipulator.
*/
enum date_stack_type {
date_stack_type_date,
date_stack_type_per
};
struct date_stack {
enum date_stack_type type;
union {
struct {
struct iso8601_date d;
struct iso8601_details det;
}date;
struct iso8601_elapsed per;
}u;
struct date_stack* next;
};
struct date_stack* date_stack_top = 0;
int date_stack_size = 0;
/* date_stack_push_*()
* Routines for pushing an object onto the date stack.
*/
void date_stack_push_date(const struct iso8601_date* d, const struct iso8601_details* det)
{
struct date_stack* s;
s = malloc(sizeof(struct date_stack));
s->type = date_stack_type_date;
s->u.date.d = *d;
s->u.date.det = *det;
s->next = date_stack_top;
date_stack_top = s;
++date_stack_size;
}
void date_stack_push_elapsed(const struct iso8601_elapsed* per)
{
struct date_stack* s;
s = malloc(sizeof(struct date_stack));
s->type = date_stack_type_per;
s->u.per = *per;
s->next = date_stack_top;
date_stack_top = s;
++date_stack_size;
}
/* date_stack_pop_date()
* Pops a date from the stack, returning non-0 on error (underflow or type mismatch).
*/
int date_stack_pop_date(struct iso8601_date* d, struct iso8601_details* det)
{
struct date_stack* next;
if(!date_stack_top) {
fputs("Stack underflow.\n", stderr);
return -1;
}
if(date_stack_top->type != date_stack_type_date) {
fputs("Type mismatch.\n", stderr);
return -1;
}
*d = date_stack_top->u.date.d;
*det = date_stack_top->u.date.det;
--date_stack_size;
next = date_stack_top->next;
free(date_stack_top);
date_stack_top = next;
return 0;
}
/* date_stack_pop_elapsed()
* Pops an elapsed period from the stack, returning non-0 on error (underflow or type mismatch).
*/
int date_stack_pop_elapsed(struct iso8601_elapsed* per)
{
struct date_stack* next;
if(!date_stack_top) {
fputs("Stack underflow.\n", stderr);
return -1;
}
if(date_stack_top->type != date_stack_type_per) {
fputs("Type mismatch.\n", stderr);
return -1;
}
*per = date_stack_top->u.per;
--date_stack_size;
next = date_stack_top->next;
free(date_stack_top);
date_stack_top = next;
return 0;
}
/* usage()
* Displays help.
*/
void usage(void)
{
fputs("Push a date or elapsed time onto the stack by entering it, e.g.:\n"
" 2007-07-29 12345.67\n"
"Operators:\n"
" + (date, period) Advance date by elapsed time, push date.\n"
" - (date, period) Regress date by elapsed time, push date.\n"
" < (date, date) Prints 1 if first date less than second.\n"
" <= (date, date) Prints 1 if first date less than or equal to second.\n"
" == (date, date) Prints 1 if dates equal.\n"
" p (date) Prints a date.\n"
"\n"
"Type help for this screen at any point.\n"
"\n", stdout);
}
int parse_command(char* cmd)
{
struct iso8601_date date, date2;
struct iso8601_details details, details2;
struct iso8601_elapsed period;
double period_d;
char date_str[40];
int ret = 0;
cmd = strtok(cmd, " \n");
while(cmd) {
if(!iso8601_parse(cmd, &date, 0, &details)) {
date_stack_push_date(&date, &details);
} else if(!safe_strtod(cmd, &period_d) && period_d >= 0) {
period.sec = trunc(period_d);
period.nsec = trunc((period_d - period.sec) * 1e9);
date_stack_push_elapsed(&period);
} else if(!strcmp(cmd, "+")) {
if(date_stack_pop_elapsed(&period) || date_stack_pop_date(&date, &details)) {
ret = 1;
} else {
iso8601_add_elapsed(&date, &period);
date_stack_push_date(&date, &details);
}
} else if(!strcmp(cmd, "-")) {
if(date_stack_pop_elapsed(&period) || date_stack_pop_date(&date, &details)) {
ret = 1;
} else {
iso8601_subtract_elapsed(&date, &period);
date_stack_push_date(&date, &details);
}
} else if(!strcmp(cmd, "<")) {
if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) {
ret = 1;
} else {
printf("%d\n", iso8601_lt(&date2, &date));
}
} else if(!strcmp(cmd, "<=")) {
if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) {
ret = 1;
} else {
printf("%d\n", iso8601_lte(&date2, &date));
}
} else if(!strcmp(cmd, "==")) {
if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) {
ret = 1;
} else {
printf("%d\n", iso8601_eq(&date2, &date));
}
} else if(!strcmp(cmd, "p")) {
if(date_stack_pop_date(&date, &details)) {
ret = 1;
} else {
iso8601_print(date_str, sizeof(date_str), &date, &details);
fputs(date_str, stdout);
putc('\n', stdout);
}
} else {
fputs("Unrecognised command or argument.\n", stderr);
ret = 1;
}
cmd = strtok(0, " \n");
}
return ret;
}
int main(int argc, char* argv[])
{
int ret = 0;
char* buf = 0;
size_t buf_len = 0;
if(argc == 2 && !strcmp(argv[1], "--print-summary")) {
printf("Interactive date manipulator. Also evaluates commandline.\n");
return 0;
}
/* commandline -- evaluate arguments as though interactive */
if(argc > 1) {
for(ret = 1; ret < argc; ++ret) {
if(parse_command(argv[ret])) return 1;
}
return 0;
}
usage();
while(!feof(stdin)) {
printf("%d >> ", date_stack_size);
ret = getline(&buf, &buf_len, stdin);
if(ret == -1) break;
parse_command(buf);
}
return 0;
}
/* options for text editors
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
vim: expandtab:ts=4:sw=4
*/