aboutsummaryrefslogtreecommitdiff
path: root/src/value.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/value.c')
-rw-r--r--src/value.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/src/value.c b/src/value.c
new file mode 100644
index 0000000..289aa0f
--- /dev/null
+++ b/src/value.c
@@ -0,0 +1,244 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#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;
+}