#include #include #include #include "common.h" #include "value.h" // TODO: add the other types // save data as a symbol // return 0 on sucess, < 0 on error static int symbol_save(char **symbol, char *data); // same as value_string static char *symbol_string(char *symbol, char *buf, size_t buf_sz); // try to save data as a literal // returns 0 on sucess, > 0 on fail (data isnt a possible literal), // and < 0 on error static int literal_save(struct literal *literal, char *data); static void literal_free(struct literal *literal); // same as value_string static char *literal_string(struct literal *literal, char *buf, size_t buf_sz); // for a function that saves string data as a value #define S_FN(fn, arg) fn(arg, (char *)data) // for a function that saves a value as a string in buf[buf_sz] #define P_FN(fn, arg) fn(arg, buf, buf_sz) #define MANAGE_VALUE_TBL(X, value) \ /* X(type, save function, \ how to free, how to save to string) */ \ X(VALUE_SYMBOL, S_FN(symbol_save, &value->symbol), \ free(value->symbol), P_FN(symbol_string, value->symbol)) \ X(VALUE_LITERAL, S_FN(literal_save, &value->literal), \ literal_free(&value->literal), P_FN(literal_string, &value->literal)) \ // X(VALUE_PAIR, ...) // X(VALUE_LIST, ...) // X(VALUE_PROC, ...) // ------ Exported Functions Implementations ------- // // ------------------------------------------------- // #define CASE_SAVE(type, save_func, free_func, string_func) \ case type: *ret = save_func; break; value_t value_create(enum value_type type, void *data, int *ret) { value_t value = xmalloc(sizeof(struct value)); value->type = VALUE_TYPES; value->references = 1; switch(type) { MANAGE_VALUE_TBL(CASE_SAVE, value); default: err("value_create: Unknown value type given"); *ret = -1; break; } if(!*ret) { value->type = type; return value; } value_destroy(value); return NULL; } value_t value_copy(value_t value) { value->references++; return value; } #define CASE_FREE(type, save_func, free_func, string_func) \ case type: free_func; break; void value_destroy(value_t value) { if(!value) return; if(--value->references > 0) { return; } switch(value->type) { MANAGE_VALUE_TBL(CASE_FREE, value); default: break; } free(value); } #define CASE_STRING(type, save_func, free_func, string_func) \ case type: return string_func; char *value_string(value_t value, char *buf, size_t buf_sz) { switch(value->type) { MANAGE_VALUE_TBL(CASE_STRING, value); default: return NULL; } } // ---------------- Symbol Functions --------------- // static int symbol_save(char **symbol, char *data) { size_t len = strlen(data) + 1; *symbol = xmalloc(len); memcpy(*symbol, data, len); return 0; } static char *symbol_string(char *symbol, char *buf, size_t buf_sz) { size_t len = strlen(symbol) + 1; if(len> buf_sz) { return NULL; } memcpy(buf, symbol, len); return buf; } // --------------- Literal Functions --------------- // // write a value in a printf format to buf[buf_sz] #define EX(...) (((size_t)snprintf(buf, buf_sz, __VA_ARGS__) > buf_sz) ? NULL : buf) #define MANAGE_LITERAL_TBL(X, literal) \ /* X(type, how to free how to print) */ \ X(LITERAL_STRING, free(literal->string), EX("%s", literal->string)) \ X(LITERAL_NUM_INT, ;, EX("%ld", literal->num_int)) \ X(LITERAL_NUM_FLOAT, ;, EX("%f", literal->num_float)) // try to convert the data to a literal // returns 0 on success, > 0 on fail (data isnt a literal), // and < 0 on error static int literal_try_string(struct literal *literal, char *data); static int literal_try_int (struct literal *literal, char *data); static int literal_try_float (struct literal *literal, char *data); // list of the try_type functions (order matters) typedef int (*try_func)(struct literal *, char *); try_func literal_try_funcs[] = { literal_try_string, literal_try_int, literal_try_float, }; static int literal_save(struct literal *literal, char *data) { int ret = 0; for(size_t i = 0; i < sizeof(literal_try_funcs)/sizeof(try_func); i++) { // <= 0 means that it either failed fatally, or succeeded if((ret = literal_try_funcs[i](literal, data)) <= 0) break; } return ret; } #define CASE_LITERAL_FREE(type, free_func, string_func) \ case type: free_func; break; static void literal_free(struct literal *literal) { switch(literal->type) { MANAGE_LITERAL_TBL(CASE_LITERAL_FREE, literal); default: break; } } #define CASE_LITERAL_STRING(type, free_func, string_func) \ case type: return string_func; static char *literal_string(struct literal *literal, char *buf, size_t buf_sz) { switch(literal->type) { MANAGE_LITERAL_TBL(CASE_LITERAL_STRING, literal); default: return NULL; break; } } // Literal try_type functions static int literal_try_string(struct literal *literal, char *data) { if(data[0] != '"') return 1; literal->type = LITERAL_STRING; size_t len = strlen(data); // no +1 needed since first character is discarded literal->string = xmalloc(len); memcpy(literal->string, data + 1, len); return 0; } static int literal_try_int(struct literal *literal, char *data) { errno = ERANGE + 1; // set errno to not ERANGE char *endptr; long num = strtol(data, &endptr, 10); if(*endptr != '\0') { // the whole string isn't a number return 1; } if(errno == ERANGE) { warn("Given integer literal %s is outside the possible range", data); } literal->type = LITERAL_NUM_INT; literal->num_int = num; return 0; } static int literal_try_float (struct literal *literal, char *data) { errno = ERANGE + 1; // set errno to not ERANGE char *endptr; float num = strtof(data, &endptr); if(*endptr != '\0') { // the whole string isn't a number return 1; } if(errno == ERANGE) { warn("Given float literal %s is outside the possible range", data); } literal->type = LITERAL_NUM_FLOAT; literal->num_float = num; return 0; }