diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2012-10-14 16:21:16 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2012-10-14 16:25:44 +0300 |
commit | ffe010595a025ec6d9f0e2c248548b3dc2050cf7 (patch) | |
tree | 60ec5e69e32b980dd2b527105c296f0b19d7aad8 /lib | |
parent | 4253839e72aebd5a71684a13d5a40d70cb34e593 (diff) | |
download | eclat-ffe010595a025ec6d9f0e2c248548b3dc2050cf7.tar.gz eclat-ffe010595a025ec6d9f0e2c248548b3dc2050cf7.tar.bz2 |
Improve the sort function.
* grecs: Upgrade.
* lib/forlan.c (strtots): New function.
(func_sort): Rewrite. The "sort" built-in takes two
optional arguments: a path to the node to use as a key,
and a string of flags that set the comparison function and
sorting order.
* lib/forlangrm.y: Bugfix in argument verification code.
* src/dscrsnap-cl.opt: Document filters.
* tests/sortnum.at: New file.
* tests/sortrevnum.at: New file.
* tests/sortstring.at: New file.
* tests/sortts.at: New file.
* tests/Makefile.am: Add new test cases.
* tests/testsuite.at: Likewise.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/forlan.c | 150 | ||||
-rw-r--r-- | lib/forlangrm.y | 2 |
2 files changed, 132 insertions, 20 deletions
diff --git a/lib/forlan.c b/lib/forlan.c index 6b10cd7..7a9dab1 100644 --- a/lib/forlan.c +++ b/lib/forlan.c @@ -54,6 +54,23 @@ forlan_node_create(enum forlan_type type) static void f_dump_node(FILE *fp, union forlan_node *p, int *num, int lev); +static unsigned long +strtots(const char *input) +{ + struct tm tm; + time_t t; + + /* 2012-06-21T10:36:48.000Z */ + memset(&tm, 0, sizeof(tm)); + if (sscanf(input, "%d-%2d-%2dT%2d:%2d:%2d.", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) + die(EX_DATAERR, "invalid timestamp: %s", input); + tm.tm_year -= 1900; + tm.tm_mon--; + return mktime(&tm); +} + static void free_value(void *p) { @@ -867,20 +884,127 @@ func_parent(forlan_eval_env_t env, struct grecs_list *list) env->retval.v.node = val->v.node->up; } } + +/* Work over a sort of bug in GRECS: there's no way to pass user-defined + data to the comparator function */ +static char *forlan_sort_key; +static int (*forlan_compar)(const char *, const char *); +static int forlan_sort_reverse; + +static int +forlan_compar_string(const char *a, const char *b) +{ + return strcmp(a, b); +} + +static int +forlan_compar_string_ci(const char *a, const char *b) +{ + return strcasecmp(a, b); +} + +static int +forlan_compar_numeric(const char *a, const char *b) +{ + unsigned long na, nb; + char *p; + + na = strtoul(a, &p, 0); + if (*p) + return forlan_compar_string(a, b); + nb = strtoul(b, &p, 0); + if (*p) + return forlan_compar_string(a, b); + if (na < nb) + return -1; + if (na > nb) + return 1; + return 0; +} static int -node_ident_cmp(struct grecs_node const *a, struct grecs_node const *b) +forlan_compar_timestamp(const char *a, const char *b) { - return strcmp(a->ident, b->ident); + unsigned long na = strtots(a), nb = strtots(b); + if (na < nb) + return -1; + if (na > nb) + return 1; + return 0; +} + +static int +forlan_node_cmp(struct grecs_node const *a, struct grecs_node const *b) +{ + int rc; + + struct grecs_node const *sa, *sb; + if (!forlan_sort_key) + rc = strcmp(a->ident, b->ident); + else { + sa = grecs_find_node((struct grecs_node *)a, forlan_sort_key); + sb = grecs_find_node((struct grecs_node *)b, forlan_sort_key); + if (!sa || !sb || sa->type != grecs_node_stmt || + sb->type != grecs_node_stmt || + sa->v.value->type != GRECS_TYPE_STRING || + sb->v.value->type != GRECS_TYPE_STRING) + rc = 0; + else + rc = forlan_compar(sa->v.value->v.string, + sb->v.value->v.string); + } + if (forlan_sort_reverse) + rc = -rc; + return rc; } -/* FIXME */ static void func_sort(forlan_eval_env_t env, struct grecs_list *list) { struct forlan_value *val = list->head->data; - if (val->v.node) - grecs_tree_sort(val->v.node, node_ident_cmp); + struct forlan_value *t; + struct grecs_list_entry *ep; + + if (!val->v.node) + return; + + forlan_sort_key = NULL; + forlan_compar = forlan_compar_string; + forlan_sort_reverse = 0; + + if (ep = list->head->next) { + t = ep->data; + forlan_sort_key = t->v.string; + if (ep = ep->next) { + char *p; + + t = ep->data; + for (p = t->v.string; *p; p++) { + switch (*p) { + case 'n': + forlan_compar = forlan_compar_numeric; + break; + case 'i': + forlan_compar = forlan_compar_string_ci; + break; + case 't': + forlan_compar = forlan_compar_timestamp; + break; + case 's': + forlan_compar = forlan_compar_string; + break; + case 'r': + forlan_sort_reverse = 1; + break; + default: + /* FIXME: silently ignored */ + break; + } + } + } + } + + grecs_tree_sort(val->v.node, forlan_node_cmp); } static void @@ -938,21 +1062,9 @@ static void func_timestamp(forlan_eval_env_t env, struct grecs_list *list) { struct forlan_value *val = list->head->data; - char *input = val->v.string; - struct tm tm; char *buf = NULL; size_t size = 0; - time_t t; - - /* 2012-06-21T10:36:48.000Z */ - memset(&tm, 0, sizeof(tm)); - if (sscanf(val->v.string, "%d-%2d-%2dT%2d:%2d:%2d.", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, - &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) - die(EX_DATAERR, "invalid timestamp: %s", val->v.string); - tm.tm_year -= 1900; - tm.tm_mon--; - t = mktime(&tm); + unsigned long t = strtots(val->v.string); grecs_asprintf(&buf, &size, "%lu", (unsigned long)t); env->retval.v.string = buf; } @@ -963,7 +1075,7 @@ static struct forlan_function functab[] = { { "print", forlan_value_void, "", 1, -1, func_print }, { "error", forlan_value_void, "", 1, -1, func_error }, { "parent", forlan_value_node, "n", 1, 1, func_parent }, - { "sort", forlan_value_void, "n", 1, 1, func_sort }, + { "sort", forlan_value_void, "nss", 1, 3, func_sort }, { "decode", forlan_value_literal, "s", 1, 1, func_decode }, { "exit", forlan_value_void, "s", 1, 1, func_exit }, { "empty", forlan_value_boolean, "n", 1, 1, func_empty }, diff --git a/lib/forlangrm.y b/lib/forlangrm.y index 4d77df6..025fb9d 100644 --- a/lib/forlangrm.y +++ b/lib/forlangrm.y @@ -268,7 +268,7 @@ funcall : IDENT '(' ')' $1); YYERROR; } - if (fp->maxargs >= 0 && $3->count > fp->minargs) { + if (fp->maxargs >= 0 && $3->count > fp->maxargs) { grecs_error(&@1, 0, "too many arguments in call to \"%s\"", $1); |