#include #include #include "common.h" #include "value.h" #include "lexer.h" // TODO: // - create VALUE_MANAGE_TABLE which manages // both creation and destruction // - check the buffer size in cons creation // FIX: // - remove warning for void pointer cast at line 30 #define NOT_IMPLEMENTED() die("Not Implemented. ABORTING") const char * const value_type_string[] = { VALUE_TYPES(TO_STRING) }; #define VALUE(_value) (_value)->value #define FN(fn, ...) fn(buf, buf_sz, __VA_ARGS__) #define VALUE_STRING_TABLE(X, v, buf, buf_sz) \ X(VALUE_NIL, FN(snprintf, "(nil)")) \ X(VALUE_ATOM, FN(snprintf, "%s", VALUE(v).atom)) \ X(VALUE_STR, FN(snprintf, "%s", VALUE(v).str)) \ X(VALUE_INT, FN(snprintf, "%d", VALUE(v).num)) \ X(VALUE_CONS, FN(cons_print, &VALUE(v).cons)) \ X(VALUE_PROC, FN(proc_print, &VALUE(v).proc)) \ X(VALUE_PROC_BUILTIN, \ FN(snprintf, "%p", *(void **)&VALUE(v).proc_builtin.proc)) #define NOREFS(value) (--(value)->refs == 0) #define VALUE_DESTROY_TABLE(X, v) \ X(VALUE_NIL, (void)NOREFS(v)) \ X(VALUE_ATOM, if(NOREFS(v)) free(VALUE(v).atom)) \ X(VALUE_STR, if(NOREFS(v)) free(VALUE(v).str)) \ X(VALUE_INT, (void)NOREFS(v)) \ X(VALUE_CONS, (void)NOREFS(v); \ value_destroy(VALUE(v).cons.left); \ value_destroy(VALUE(v).cons.right)) \ X(VALUE_PROC, if(NOREFS(v)) proc_destroy(&VALUE(v).proc)) \ X(VALUE_PROC_BUILTIN, (void)NOREFS(v)) #define CASE_RETURN_APPLY(vtype, apply) \ case vtype: return apply; #define CASE_APPLY_BREAK(vtype, apply) \ case vtype: apply; break; static int cons_print(char *buf, size_t buf_sz, struct cons *cons); static int proc_print(char *buf, size_t buf_sz, struct proc *proc); // static value_t proc_create(...); static void proc_destroy(struct proc *proc); value_t value_create(enum value_type type, void *value) { value_t _value = malloc(sizeof(*_value)); _value->type = type; if(value != NULL) _value->value = *(union value_union *)value; _value->refs = 1; return _value; } void value_destroy(value_t value) { if(!value) return; switch(value->type) { VALUE_DESTROY_TABLE(CASE_APPLY_BREAK, value); } if(value->refs == 0) free(value); } #define STR_ALLOC_COPY(dest, str) do { \ size_t len = strlen(str) + 1; \ dest = malloc(len); \ memcpy((dest), (str), len); \ } while(0) value_t value_from_token(struct token *token) { switch(token->type) { case TOKEN_ID: ; char *atom = NULL; STR_ALLOC_COPY(atom, token->value.id); return value_create(VALUE_ATOM, &atom); case TOKEN_STR: ; char *str = NULL; STR_ALLOC_COPY(str, token->value.str); return value_create(VALUE_STR, &str); case TOKEN_INT: return value_create(VALUE_INT, &token->value); default: err("Cannot turn token '%s' to a value", token_type_string[token->type]); return VALUE_EMPTY; } } value_t value_copy(value_t value) { if(!value) return value; value->refs++; if(value->type == VALUE_CONS) { value_copy(value->value.cons.left); value_copy(value->value.cons.right); } return value; } int value_string(value_t value, size_t buf_sz, char *buf) { if(!value) return snprintf(buf, buf_sz, "(empty)"); switch(value->type) { VALUE_STRING_TABLE(CASE_RETURN_APPLY, value, buf, buf_sz) } return 0; } static int cons_print(char *buf, size_t buf_sz, struct cons *cons) { // TODO: check for size and off by one errors int offset = 0; buf[offset++] = '('; offset += value_string(cons->left, buf_sz-offset, buf+offset); buf[offset++] = ' '; buf[offset++] = '.'; buf[offset++] = ' '; offset += value_string(cons->right, buf_sz-offset, buf+offset); buf[offset++] = ')'; return offset; } static int proc_print(char *buf, size_t buf_sz, struct proc *proc) { return 0; (void)buf; (void)buf_sz; (void)proc; NOT_IMPLEMENTED(); } static void proc_destroy(struct proc *proc) { for(size_t i = 0; i < proc->argc; i++) value_destroy(proc->arg_keys[i]); free(proc->arg_keys); for(size_t i = 0; i < proc->body_len; i++) token_dealloc(&proc->body[i]); free(proc->body); env_destroy(proc->parent_env); }