/* @@@HEAD@@@
// Routines for ColdC data manipulation.
*/
#include <stdlib.h>
#include <ctype.h>
#include "config.h"
#include "defs.h"
#include "y.tab.h"
#include "cdc_types.h"
#include "data.h"
#include "util.h"
#include "cache.h"
#include "memory.h"
#include "token.h"
#include "log.h"
#include "lookup.h"
/* Effects: Returns 0 if and only if d1 and d2 are equal according to ColdC
* conventions. If d1 and d2 are of the same type and are integers or
* strings, returns greater than 0 if d1 is greater than d2 according
* to ColdC conventions, and less than 0 if d1 is less than d2. */
int data_cmp(data_t *d1, data_t *d2) {
if (d1->type == FLOAT && d2->type == INTEGER) {
d2->type = FLOAT;
d2->u.fval = (float) d2->u.val;
} else if (d1->type == INTEGER && d2->type == FLOAT) {
d1->type = FLOAT;
d1->u.fval = (float) d1->u.val;
}
if (d1->type != d2->type) {
return 1;
}
switch (d1->type) {
case INTEGER:
return d1->u.val - d2->u.val;
case FLOAT: {
float t=d1->u.fval - d2->u.fval;
return (t>0 ? 1 : (t==0 ? 0 : -1));
}
case STRING:
return strccmp(string_chars(d1->u.str), string_chars(d2->u.str));
case DBREF:
return (d1->u.dbref != d2->u.dbref);
case LIST:
return list_cmp(d1->u.list, d2->u.list);
case SYMBOL:
return (d1->u.symbol != d2->u.symbol);
case ERROR:
return (d1->u.error != d2->u.error);
case FROB:
if (d1->u.frob->cclass != d2->u.frob->cclass)
return 1;
return data_cmp(&d1->u.frob->rep, &d2->u.frob->rep);
case DICT:
return dict_cmp(d1->u.dict, d2->u.dict);
case BUFFER:
if (d1->u.buffer == d2->u.buffer)
return 0;
if (d1->u.buffer->len != d2->u.buffer->len)
return 1;
return MEMCMP(d1->u.buffer->s, d2->u.buffer->s, d1->u.buffer->len);
default:
return 1;
}
}
/* Effects: Returns 1 if data is true according to ColdC conventions, or 0 if
* data is false. */
int data_true(data_t *d)
{
switch (d->type) {
case INTEGER:
return (d->u.val != 0);
case FLOAT:
return (d->u.fval != 0.0);
case STRING:
return (string_length(d->u.str) != 0);
case DBREF:
return 1;
case LIST:
return (list_length(d->u.list) != 0);
case SYMBOL:
return 1;
case ERROR:
return 0;
case FROB:
return 1;
case DICT:
return (d->u.dict->keys->len != 0);
case BUFFER:
return (d->u.buffer->len != 0);
default:
return 0;
}
}
unsigned long data_hash(data_t *d)
{
list_t *values;
switch (d->type) {
case INTEGER:
return d->u.val;
case FLOAT:
return *((long*)(&d->u.fval));
case STRING:
return hash_case(string_chars(d->u.str), string_length(d->u.str));
case DBREF:
return d->u.dbref;
case LIST:
if (list_length(d->u.list) > 0)
return data_hash(list_first(d->u.list));
else
return 100;
case SYMBOL:
return hash(ident_name(d->u.symbol));
case ERROR:
return hash(ident_name(d->u.error));
case FROB:
return d->u.frob->cclass + data_hash(&d->u.frob->rep);
case DICT:
values = d->u.dict->values;
if (list_length(values) > 0)
return data_hash(list_first(values));
else
return 200;
case BUFFER:
if (d->u.buffer->len)
return d->u.buffer->s[0] + d->u.buffer->s[d->u.buffer->len - 1];
else
return 300;
default:
panic("data_hash() called with invalid type");
return -1;
}
}
/* Modifies: dest.
* Effects: Copies src into dest, updating reference counts as necessary. */
void data_dup(data_t *dest, data_t *src)
{
dest->type = src->type;
switch (src->type) {
case INTEGER:
dest->u.val = src->u.val;
break;
case FLOAT:
dest->u.fval = src->u.fval;
break;
case STRING:
dest->u.str = string_dup(src->u.str);
break;
case DBREF:
dest->u.dbref = src->u.dbref;
break;
case LIST:
dest->u.list = list_dup(src->u.list);
break;
case SYMBOL:
dest->u.symbol = ident_dup(src->u.symbol);
break;
case ERROR:
dest->u.error = ident_dup(src->u.error);
break;
case FROB:
dest->u.frob = TMALLOC(Frob, 1);
dest->u.frob->cclass = src->u.frob->cclass;
data_dup(&dest->u.frob->rep, &src->u.frob->rep);
break;
case DICT:
dest->u.dict = dict_dup(src->u.dict);
break;
case BUFFER:
dest->u.buffer = buffer_dup(src->u.buffer);
break;
}
}
/* Modifies: The value referred to by data.
* Effects: Updates the reference counts for the value referred to by data
* when we are no longer using it. */
void data_discard(data_t *data)
{
switch (data->type) {
case STRING:
string_discard(data->u.str);
break;
case LIST:
list_discard(data->u.list);
break;
case SYMBOL:
ident_discard(data->u.symbol);
break;
case ERROR:
ident_discard(data->u.error);
break;
case FROB:
data_discard(&data->u.frob->rep);
TFREE(data->u.frob, 1);
break;
case DICT:
dict_discard(data->u.dict);
break;
case BUFFER:
buffer_discard(data->u.buffer);
}
}
string_t *data_tostr(data_t *data)
{
char *s;
Number_buf nbuf;
switch (data->type) {
case INTEGER:
s = long_to_ascii(data->u.val, nbuf);
return string_from_chars(s, strlen(s));
case FLOAT:
s = float_to_ascii(data->u.fval,nbuf);
return string_from_chars(s, strlen(s));
case STRING:
return string_dup(data->u.str);
case DBREF:
s = long_to_ascii(data->u.dbref, nbuf);
return string_add_chars(string_from_chars("#", 1), s, strlen(s));
case LIST:
return string_from_chars("[list]", 6);
case SYMBOL:
s = ident_name(data->u.symbol);
return string_from_chars(s, strlen(s));
case ERROR:
s = ident_name(data->u.error);
return string_from_chars(s, strlen(s));
case FROB:
return string_from_chars("<frob>", 6);
case DICT:
return string_from_chars("#[dict]", 7);
case BUFFER:
return string_from_chars("`[buffer]", 9);
default:
panic("Unrecognized data type.");
return NULL;
}
}
/* Effects: Returns a string containing a printed representation of data. */
string_t *data_to_literal(data_t *data)
{
string_t *str = string_new(0);
return data_add_literal_to_str(str, data);
}
string_t *data_add_list_literal_to_str(string_t *str, list_t *list)
{
data_t *d, *next;
str = string_addc(str, '[');
d = list_first(list);
if (d) {
next = list_next(list, d);
while (next) {
str = data_add_literal_to_str(str, d);
str = string_add_chars(str, ", ", 2);
d = next;
next = list_next(list, d);
}
str = data_add_literal_to_str(str, d);
}
return string_addc(str, ']');
}
/* Modifies: str (mutator, claims reference count).
* Effects: Returns a string with the printed representation of data added to
* it. */
string_t *data_add_literal_to_str(string_t *str, data_t *data)
{
char *s;
Number_buf nbuf;
int i;
switch(data->type) {
case INTEGER:
s = long_to_ascii(data->u.val, nbuf);
return string_add_chars(str, s, strlen(s));
case FLOAT:
s=float_to_ascii(data->u.fval,nbuf);
return string_add_chars(str, s, strlen(s));
case STRING:
s = string_chars(data->u.str);
return string_add_unparsed(str, s, string_length(data->u.str));
case DBREF:
s = long_to_ascii(data->u.dbref, nbuf);
str = string_addc(str, '#');
return string_add_chars(str, s, strlen(s));
case LIST:
return data_add_list_literal_to_str(str, data->u.list);
case SYMBOL:
str = string_addc(str, '\'');
s = ident_name(data->u.symbol);
if (*s && is_valid_ident(s))
return string_add_chars(str, s, strlen(s));
else
return string_add_unparsed(str, s, strlen(s));
case ERROR:
str = string_addc(str, '~');
s = ident_name(data->u.error);
if (is_valid_ident(s))
return string_add_chars(str, s, strlen(s));
else
return string_add_unparsed(str, s, strlen(s));
case FROB:
str = string_add_chars(str, "<#", 2);
s = long_to_ascii(data->u.frob->cclass, nbuf);
str = string_add_chars(str, s, strlen(s));
str = string_add_chars(str, ", ", 2);
str = data_add_literal_to_str(str, &data->u.frob->rep);
return string_addc(str, '>');
case DICT:
return dict_add_literal_to_str(str, data->u.dict);
case BUFFER:
str = string_add_chars(str, "`[", 2);
for (i = 0; i < data->u.buffer->len; i++) {
s = long_to_ascii(data->u.buffer->s[i], nbuf);
str = string_add_chars(str, s, strlen(s));
if (i < data->u.buffer->len - 1)
str = string_add_chars(str, ", ", 2);
}
return string_addc(str, ']');
default:
return str;
}
}
char *data_from_literal(data_t *d, char *s)
{
while (isspace(*s))
s++;
d->type = -1;
if (isdigit(*s) || (*s == '-' && isdigit(s[1]))) {
char *t = s;
d->type = INTEGER;
d->u.val = atol(s);
while (isdigit(*++s));
if (*s=='.' || *s=='e') {
d->type = FLOAT;
d->u.fval = atof(t);
s++;
while (isdigit(*s) || *s == '.' || *s == 'e' || *s == '-') s++;
}
return s;
} else if (*s == '"') {
d->type = STRING;
d->u.str = string_parse(&s);
return s;
} else if (*s == '#' && (isdigit(s[1]) || s[1] == '-')) {
d->type = DBREF;
d->u.dbref = atol(++s);
while (isdigit(*++s));
return s;
} else if (*s == '$') {
long name, dbref;
s++;
name = parse_ident(&s);
if (!lookup_retrieve_name(name, &dbref))
dbref = -1;
ident_discard(name);
d->type = DBREF;
d->u.dbref = dbref;
return s;
} else if (*s == '[') {
list_t *list;
list = list_new(0);
s++;
while (*s && *s != ']') {
s = data_from_literal(d, s);
if (d->type == -1) {
list_discard(list);
d->type = -1;
return s;
}
list = list_add(list, d);
data_discard(d);
while (isspace(*s))
s++;
if (*s == ',')
s++;
while (isspace(*s))
s++;
}
d->type = LIST;
d->u.list = list;
return (*s) ? s + 1 : s;
} else if (*s == '#' && s[1] == '[') {
data_t assocs;
/* Get associations. */
s = data_from_literal(&assocs, s + 1);
if (assocs.type != LIST) {
if (assocs.type != -1)
data_discard(&assocs);
d->type = -1;
return s;
}
/* Make a dict from the associations. */
d->type = DICT;
d->u.dict = dict_from_slices(assocs.u.list);
data_discard(&assocs);
if (!d->u.dict)
d->type = -1;
return s;
} else if (*s == '`' && s[1] == '[') {
data_t *p, byte_data;
list_t *bytes;
Buffer *buf;
int i;
/* Get the contents of the buffer. */
s = data_from_literal(&byte_data, s + 1);
if (byte_data.type != LIST) {
if (byte_data.type != -1)
data_discard(&byte_data);
return s;
}
bytes = byte_data.u.list;
/* Verify that the bytes are numbers. */
for (p = list_first(bytes); p; p = list_next(bytes, p)) {
if (p->type != INTEGER) {
data_discard(&byte_data);
return s;
}
}
/* Make a buffer from the numbers. */
buf = buffer_new(list_length(bytes));
i = 0;
for (p = list_first(bytes); p; p = list_next(bytes, p))
buf->s[i++] = p->u.val;
data_discard(&byte_data);
d->type = BUFFER;
d->u.buffer = buf;
return s;
} else if (*s == '\'') {
s++;
d->type = SYMBOL;
d->u.symbol = parse_ident(&s);
return s;
} else if (*s == '~') {
s++;
d->type = ERROR;
d->u.symbol = parse_ident(&s);
return s;
} else if (*s == '<') {
data_t cclass;
s = data_from_literal(&cclass, s + 1);
if (cclass.type == DBREF) {
while (isspace(*s))
s++;
if (*s == ',')
s++;
while (isspace(*s))
s++;
d->type = FROB;
d->u.frob = TMALLOC(Frob, 1);
d->u.frob->cclass = cclass.u.dbref;
s = data_from_literal(&d->u.frob->rep, s);
if (d->u.frob->rep.type == -1) {
TFREE(d->u.frob, 1);
d->type = -1;
}
} else if (cclass.type != -1) {
data_discard(&cclass);
}
return (*s) ? s + 1 : s;
} else {
return (*s) ? s + 1 : s;
}
}
/* Effects: Returns an id (without updating reference count) for the name of
* the type given by type. */
long data_type_id(int type)
{
switch (type) {
case INTEGER: return integer_id;
case FLOAT: return float_id;
case STRING: return string_id;
case DBREF: return dbref_id;
case LIST: return list_id;
case SYMBOL: return symbol_id;
case ERROR: return error_id;
case FROB: return frob_id;
case DICT: return dictionary_id;
case BUFFER: return buffer_id;
default: panic("Unrecognized data type."); return 0;
}
}