aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2012-10-14 16:21:16 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2012-10-14 16:25:44 +0300
commitffe010595a025ec6d9f0e2c248548b3dc2050cf7 (patch)
tree60ec5e69e32b980dd2b527105c296f0b19d7aad8 /lib
parent4253839e72aebd5a71684a13d5a40d70cb34e593 (diff)
downloadeclat-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.c150
-rw-r--r--lib/forlangrm.y2
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);

Return to:

Send suggestions and report system problems to the System administrator.