/*
// Full copyright information is available in the file ../doc/CREDITS
*/
#include "defs.h"
#include <ctype.h>
#include "util.h"
#include "cache.h"
#include "token.h"
#include "lookup.h"
#include "macros.h"
INSTANCE_PROTOTYPES(handled);
cInstance class_registry[] = {
INSTANCE_INIT(handled, "a frob")
};
void register_instance (InstanceID instance, Ident id) {
class_registry[instance - FIRST_INSTANCE].id_name = id;
}
void init_instances(void) {
register_instance (HANDLED_FROB_TYPE, frob_id);
}
/* ack, hacky */
extern cObjnum get_object_name(Ident id);
/* 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(cData *d1, cData *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 OBJNUM:
return (d1->u.objnum != d2->u.objnum);
case LIST:
return list_cmp(d1->u.list, d2->u.list);
case SYMBOL:
return (d1->u.symbol != d2->u.symbol);
case T_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: {
INSTANCE_RECORD(d1->type, r);
return r->compare(d1, d2);
}
}
}
/* Effects: Returns 1 if data is true according to ColdC conventions, or 0 if
* data is false. */
Int data_true(cData *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 OBJNUM:
return 1;
case LIST:
return (list_length(d->u.list) != 0);
case SYMBOL:
return 1;
case T_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 1;
}
}
uLong data_hash(cData *d)
{
cList *values;
switch (d->type) {
case INTEGER:
return d->u.val;
case FLOAT:
return *((uLong*)(&d->u.fval));
case STRING:
return hash_string_nocase(d->u.str);
case OBJNUM:
return d->u.objnum;
case LIST:
if (list_length(d->u.list) > 0)
return data_hash(list_first(d->u.list));
else
return 100;
case SYMBOL:
return ident_hash(d->u.symbol);
case T_ERROR:
return hash_nullchar(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: {
INSTANCE_RECORD(d->type, r);
return r->hash(d);
}
}
}
/* Modifies: dest.
* Effects: Copies src into dest, updating reference counts as necessary. */
void data_dup(cData *dest, cData *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 OBJNUM:
dest->u.objnum = src->u.objnum;
break;
case LIST:
dest->u.list = list_dup(src->u.list);
break;
case SYMBOL:
dest->u.symbol = ident_dup(src->u.symbol);
break;
case T_ERROR:
dest->u.error = ident_dup(src->u.error);
break;
case FROB:
dest->u.frob = TMALLOC(cFrob, 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;
default: {
INSTANCE_RECORD(src->type, r);
r->dup(dest, src);
}
}
}
/* 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(cData *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 T_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);
case INTEGER:
case FLOAT:
case OBJNUM:
break;
default: {
INSTANCE_RECORD(data->type, r);
r->discard(data);
}
}
}
cStr *data_tostr(cData *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 OBJNUM: {
char prefix[] = {'$', (char) NULL};
Obj * obj = cache_retrieve(data->u.objnum);
if (!obj || obj->objname == -1) {
s = long_to_ascii(data->u.objnum, nbuf);
prefix[0] = '#';
} else {
s = ident_name(obj->objname);
}
cache_discard(obj);
return string_add_chars(string_from_chars(prefix, 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 T_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:
return string_from_chars("<instance>",10);
}
}
/* Effects: Returns a string containing a printed representation of data. */
cStr *data_to_literal(cData *data, Bool objnames) {
cStr *str = string_new(0);
return data_add_literal_to_str(str, data, objnames);
}
cStr *data_add_list_literal_to_str(cStr *str, cList *list, Bool objnames) {
cData *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, objnames);
str = string_add_chars(str, ", ", 2);
d = next;
next = list_next(list, d);
}
str = data_add_literal_to_str(str, d, objnames);
}
return string_addc(str, ']');
}
/* Modifies: str (mutator, claims reference count).
* Effects: Returns a string with the printed representation of data added to
* it. */
cStr *data_add_literal_to_str(cStr *str, cData *data, Bool objnames) {
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 OBJNUM: {
char pre = '$';
Obj * obj;
if (objnames) {
obj = cache_retrieve(data->u.objnum);
if (!obj || obj->objname == -1) {
if (!obj && data->u.objnum > 0)
data->u.objnum = -data->u.objnum;
s = long_to_ascii(data->u.objnum, nbuf);
pre = '#';
} else {
s = ident_name(obj->objname);
}
cache_discard(obj);
} else {
pre = '#';
s = long_to_ascii(data->u.objnum, nbuf);
}
str = string_addc(str, pre);
return string_add_chars(str, s, strlen(s));
}
case LIST:
return data_add_list_literal_to_str(str, data->u.list, objnames);
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 T_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: {
cData d;
str = string_addc(str, '<');
d.type = OBJNUM;
d.u.objnum = data->u.frob->cclass;
str = data_add_literal_to_str(str, &d, objnames);
str = string_add_chars(str, ", ", 2);
str = data_add_literal_to_str(str, &data->u.frob->rep, objnames);
return string_addc(str, '>');
}
case DICT:
return dict_add_literal_to_str(str, data->u.dict, objnames);
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: {
INSTANCE_RECORD(data->type, r);
return r->addstr(str, data, objnames);
}
}
}
/* 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 OBJNUM: return objnum_id;
case LIST: return list_id;
case SYMBOL: return symbol_id;
case T_ERROR: return error_id;
case FROB: return frob_id;
case DICT: return dictionary_id;
case BUFFER: return buffer_id;
default: { INSTANCE_RECORD(type, r); return r->id_name; }
}
}
char * data_from_literal(cData *d, char *s) {
while (isspace(*s))
s++;
d->type = -1;
if (isdigit(*s) || ((*s == '-' || *s == '+') && isdigit(s[1]))) {
char *t = s;
d->type = INTEGER;
d->u.val = (Long) atol(s);
while (isdigit(*++s));
if (*s=='.' || *s=='e') {
d->type = FLOAT;
d->u.fval = (Float) atof(t);
s++;
while (isdigit(*s) ||
*s == '.' ||
*s == 'e' ||
*s == '-' ||
*s == '+')
s++;
}
return s;
} else if (*s == '"') {
d->type = STRING;
d->u.str = string_parse(&s);
return s;
} else if (*s == '$') {
Ident name;
cObjnum objnum;
s++;
name = parse_ident(&s);
objnum = get_object_name(name);
ident_discard(name);
d->type = OBJNUM;
d->u.objnum = objnum;
return s;
} else if (*s == '[') {
cList *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] == '[') {
cData 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++;
d->type = OBJNUM;
d->u.objnum = (cObjnum) strtol(s, &s, 10);
return s;
} else if (*s == '`' && s[1] == '[') {
cData *p, byte_data;
cList *bytes;
cBuf *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 = T_ERROR;
d->u.symbol = parse_ident(&s);
return s;
} else if (*s == '<') {
cData cclass, crep;
s = data_from_literal(&cclass, s + 1);
if (cclass.type == OBJNUM) {
while (isspace(*s))
s++;
if (*s == ',')
s++;
while (isspace(*s))
s++;
s = data_from_literal(&crep, s);
if (crep.type == -1) {
d->type = -1;
return (*s) ? s + 1 : s;
}
while (isspace(*s))
s++;
if (*s == ',') {
#include "handled_frob.h"
cData chandler;
s++;
while (isspace(*s))
s++;
s = data_from_literal(&chandler, s);
if (chandler.type != SYMBOL) {
data_discard(&crep);
d->type = -1;
if (chandler.type != -1)
data_discard(&chandler);
return (*s) ? s + 1 : s;
}
d->type = (Int) HANDLED_FROB_TYPE;
d->u.instance = (void*)TMALLOC(HandledFrob, 1);
HANDLED_FROB(d)->cclass = cclass.u.objnum;
HANDLED_FROB(d)->rep = crep;
HANDLED_FROB(d)->handler = chandler.u.symbol;
return (*s) ? s + 1 : s;
}
d->type = FROB;
d->u.frob = TMALLOC(cFrob, 1);
d->u.frob->cclass = cclass.u.objnum;
d->u.frob->rep = crep;
} else if (cclass.type != -1) {
data_discard(&cclass);
}
return (*s) ? s + 1 : s;
} else {
return (*s) ? s + 1 : s;
}
}