/* listop.c: Function operators for list manipulation. */
#define _POSIX_SOURCE
#include "x.tab.h"
#include "operator.h"
#include "execute.h"
#include "data.h"
#include "ident.h"
#include "memory.h"
void op_listlen(void)
{
Data *args;
int len;
/* Accept a list to take the length of. */
if (!func_init_1(&args, LIST))
return;
/* Replace the argument with its length. */
len = args[0].u.sublist.span;
pop(1);
push_int(len);
}
void op_sublist(void)
{
int num_args, start, span;
Data *args;
/* Accept a list, an integer, and an optional integer. */
if (!func_init_2_or_3(&args, &num_args, LIST, INTEGER, INTEGER))
return;
/* Make sure range is in bounds. */
start = args[1].u.val - 1;
span = (num_args == 3) ? args[2].u.val : args[0].u.sublist.span - start;
if (start < 0) {
throw(range_id, "Start (%d) less than one", start + 1);
} else if (span < 0) {
throw(range_id, "Sublist length (%d) less than zero", span);
} else if (start + span > args[0].u.sublist.span) {
throw(range_id, "Sublist extends to %d, past end of list (length %d)",
start + span, args[0].u.sublist.span);
} else {
/* Replace first argument with sublist, and pop other arguments. */
args[0].u.sublist.start += start;
args[0].u.sublist.span = span;
pop(num_args - 1);
}
}
void op_insert(void)
{
int pos;
Data *args;
Sublist *sublist;
/* Accept a list, an integer offset, and a data value of any type. */
if (!func_init_3(&args, LIST, INTEGER, 0))
return;
pos = args[1].u.val - 1;
sublist = &args[0].u.sublist;
if (pos < 0) {
throw(range_id, "Position (%d) less than one", pos + 1);
} else if (pos > sublist->span) {
throw(range_id, "Position (%d) beyond end of list (length %d)",
pos + 1, sublist->span);
} else {
/* Modify the list and discard the offset and data. */
anticipate_assignment();
sublist->list = list_insert(sublist->list, sublist->start + pos,
&args[2]);
sublist->span++;
pop(2);
}
}
void op_replace(void)
{
int pos;
Data *args;
Sublist *sublist;
List *list;
/* Accept a list, an integer offset, and a data value of any type. */
if (!func_init_3(&args, LIST, INTEGER, 0))
return;
pos = args[1].u.val - 1;
sublist = &args[0].u.sublist;
if (pos < 0) {
throw(range_id, "Position (%d) less than one", pos + 1);
} else if (pos > sublist->span - 1) {
throw(range_id, "Position (%d) greater than length of list (%d)",
pos + 1, sublist->span);
} else {
anticipate_assignment();
/* If we don't own the list, make a copy of the sublist. */
if (sublist->list->refs != 1) {
list = sublist->list;
sublist->list = list_from_data(list->el + sublist->start,
sublist->span);
list_discard(list);
sublist->start = 0;
}
/* Replace the list element. */
data_discard(&sublist->list->el[sublist->start + pos]);
data_dup(&sublist->list->el[sublist->start + pos], &args[2]);
/* Discard the second and third argument. */
pop(2);
}
}
void op_delete(void)
{
int pos;
Data *args, *dptr;
Sublist *sublist;
/* Accept a list and an integer offset. */
if (!func_init_2(&args, LIST, INTEGER))
return;
pos = args[1].u.val - 1;
sublist = &args[0].u.sublist;
if (pos < 0) {
throw(range_id, "Position (%d) less than one", pos + 1);
} else if (pos > sublist->span - 1) {
throw(range_id, "Position (%d) greater than length of list (%d)",
pos + 1, sublist->span);
} else {
anticipate_assignment();
/* Remove the list element. */
sublist_truncate(sublist);
dptr = &sublist->list->el[sublist->start + pos];
data_discard(dptr);
MEMMOVE(dptr, dptr + 1, Data,
sublist->span - sublist->start - pos - 1);
sublist->span--;
sublist->list->len--;
/* Pop the second argument. */
pop(1);
}
}
void op_setadd(void)
{
int i;
Data *args;
Sublist *sublist;
/* Accept a list and a data value of any type. */
if (!func_init_2(&args, LIST, 0))
return;
/* If args[1] is in args[0], then just pop args[1] and return. */
for (i = 0; i < args[0].u.sublist.span; i++) {
if (data_cmp(data_dptr(&args[0]) + i, &args[1]) == 0) {
pop(1);
return;
}
}
/* Add args[1] to args[0] and pop args[1]. */
anticipate_assignment();
sublist = &args[0].u.sublist;
sublist_truncate(sublist);
sublist->list = list_add(sublist->list, &args[1]);
sublist->span++;
pop(1);
}
void op_setremove(void)
{
int i;
Data *args, *d;
Sublist *sublist;
/* Accept a list and a data value of any type. */
if (!func_init_2(&args, LIST, 0))
return;
/* Look for args[1] in args[0]. */
for (i = 0; i < args[0].u.sublist.span; i++) {
if (data_cmp(data_dptr(&args[0]) + i, &args[1]) == 0)
break;
}
/* Nothing to do if args[1] wasn't there. */
if (i == args[0].u.sublist.span) {
pop(1);
return;
}
/* Remove the list element. */
anticipate_assignment();
sublist = &args[0].u.sublist;
sublist_truncate(sublist);
d = &sublist->list->el[sublist->start + i];
data_discard(d);
MEMCPY(d, d + 1, Data, sublist->span - sublist->start - i - 1);
sublist->list->len--;
sublist->span--;
/* Pop the second argument. */
pop(1);
}
void op_union(void)
{
int i, j;
Data *args, *base1, *base2;
Sublist *sublist;
/* Accept two lists. */
if (!func_init_2(&args, LIST, LIST))
return;
anticipate_assignment();
/* Prepare to add elements to left list. */
sublist = &args[0].u.sublist;
sublist_truncate(sublist);
base1 = sublist->list->el + sublist->start;
base2 = data_dptr(&args[1]);
/* Iterate over elements in second list. */
for (i = 0; i < args[1].u.sublist.span; i++) {
/* Look for data in first sublist. */
for (j = 0; j < sublist->span; j++) {
if (data_cmp(&base1[j], &base2[i]) == 0)
break;
}
if (j == sublist->span) {
/* Data wasn't in first sublist; add it. */
sublist->list = list_add(sublist->list, &base2[i]);
sublist->span++;
}
}
/* Pop the second list. */
pop(1);
}