#include "value.h" #include #include #include typedef struct Array { int refcount; int count; Value *items; /* owns items; each item owned by array */ } Array; Value make_int(int64_t v) { Value val; val.type = VAL_INT; val.i = v; return val; } Value make_string(const char *s) { Value val; val.type = VAL_STRING; if (s) val.s = strdup(s); else val.s = strdup(""); return val; } Value make_function(struct Bytecode *fn) { Value val; val.type = VAL_FUNCTION; val.fn = fn; return val; } Value make_nil(void) { Value v; v.type = VAL_NIL; return v; } Value make_array_from_values(const Value *vals, int count) { if (count < 0) count = 0; Array *arr = (Array*)malloc(sizeof(Array)); if (!arr) { Value nil = make_nil(); return nil; } arr->refcount = 1; arr->count = count; if (count > 0) { arr->items = (Value*)malloc(sizeof(Value) * count); if (!arr->items) { free(arr); Value nil = make_nil(); return nil; } for (int i = 0; i < count; ++i) { arr->items[i] = copy_value(&vals[i]); } } else { arr->items = NULL; } Value v; v.type = VAL_ARRAY; v.arr = (struct Array*)arr; return v; } int array_length(const Value *v) { if (!v || v->type != VAL_ARRAY || !v->arr) return -1; const Array *a = (const Array*)v->arr; return a->count; } int array_get_copy(const Value *v, int index, Value *out) { if (!v || v->type != VAL_ARRAY || !v->arr) return 0; const Array *a = (const Array*)v->arr; if (index < 0 || index >= a->count) return 0; if (out) *out = copy_value(&a->items[index]); return 1; } int array_set(Value *v, int index, Value newElem) { if (!v || v->type != VAL_ARRAY || !v->arr) return 0; Array *a = (Array*)v->arr; if (index < 0 || index >= a->count) return 0; free_value(a->items[index]); a->items[index] = newElem; /* take ownership */ return 1; } static int ensure_array_capacity(Array *a, int newCount) { if (newCount <= a->count) return 1; /* grow to at least newCount; double strategy */ int curr = a->count; int cap = curr; if (cap < 4) cap = 4; while (cap < newCount) cap *= 2; Value *newItems = (Value*)realloc(a->items, sizeof(Value) * cap); if (!newItems) return 0; /* if growing beyond current count, initialize new slots to nil */ if (cap > a->count) { for (int i = a->count; i < cap; ++i) { newItems[i] = make_nil(); } } a->items = newItems; return 1; } int array_push(Value *v, Value newElem) { if (!v || v->type != VAL_ARRAY || !v->arr) return -1; Array *a = (Array*)v->arr; /* ensure capacity for count+1 by reallocating items array to at least count+1 elements */ Value *newItems = (Value*)realloc(a->items, sizeof(Value) * (a->count + 1)); if (!newItems) { free_value(newElem); return -1; } a->items = newItems; a->items[a->count] = newElem; /* take ownership */ a->count += 1; return a->count; } int array_pop(Value *v, Value *out) { if (!v || v->type != VAL_ARRAY || !v->arr) return 0; Array *a = (Array*)v->arr; if (a->count <= 0) return 0; int idx = a->count - 1; if (out) *out = a->items[idx]; /* transfer ownership */ else free_value(a->items[idx]); a->count -= 1; return 1; } int array_insert(Value *v, int index, Value newElem) { if (!v || v->type != VAL_ARRAY || !v->arr) return -1; Array *a = (Array*)v->arr; if (index < 0) index = 0; if (index > a->count) index = a->count; Value *newItems = (Value*)realloc(a->items, sizeof(Value) * (a->count + 1)); if (!newItems) { free_value(newElem); return -1; } a->items = newItems; /* shift right */ for (int i = a->count; i > index; --i) { a->items[i] = a->items[i - 1]; } a->items[index] = newElem; /* take ownership */ a->count += 1; return a->count; } int array_remove(Value *v, int index, Value *out) { if (!v || v->type != VAL_ARRAY || !v->arr) return 0; Array *a = (Array*)v->arr; if (index < 0 || index >= a->count) return 0; if (out) *out = a->items[index]; /* transfer ownership */ else free_value(a->items[index]); /* shift left */ for (int i = index; i < a->count - 1; ++i) { a->items[i] = a->items[i + 1]; } a->count -= 1; return 1; } Value array_slice(const Value *v, int start, int end) { if (!v || v->type != VAL_ARRAY || !v->arr) return make_nil(); const Array *a = (const Array*)v->arr; int n = a->count; if (start < 0) start = 0; if (end < 0 || end > n) end = n; if (start > end) start = end; int m = end - start; if (m <= 0) { return make_array_from_values(NULL, 0); } return make_array_from_values(a->items + start, m); } Value array_concat(const Value *av, const Value *bv) { if (!av || !bv || av->type != VAL_ARRAY || bv->type != VAL_ARRAY) return make_nil(); const Array *a = (const Array*)av->arr; const Array *b = (const Array*)bv->arr; int na = a ? a->count : 0; int nb = b ? b->count : 0; int total = na + nb; if (total <= 0) return make_array_from_values(NULL, 0); Value *tmp = (Value*)malloc(sizeof(Value) * total); if (!tmp) return make_nil(); for (int i = 0; i < na; ++i) tmp[i] = a->items[i]; for (int j = 0; j < nb; ++j) tmp[na + j] = b->items[j]; Value out = make_array_from_values(tmp, total); /* free temporaries we copied from (deep copy in make_array_from_values) */ free(tmp); return out; } Value copy_value(const Value *v) { Value out; out.type = v->type; switch (v->type) { case VAL_INT: out.i = v->i; break; case VAL_STRING: out.s = v->s ? strdup(v->s) : strdup(""); break; case VAL_FUNCTION: out.fn = v->fn; /* shallow copy pointer */ break; case VAL_ARRAY: { Array *a = (Array*)v->arr; out.arr = (struct Array*)a; if (a) a->refcount++; break; } case VAL_NIL: default: break; } return out; } /* deep copy including arrays (recursively copies items) */ Value deep_copy_value(const Value *v) { switch (v->type) { case VAL_INT: return make_int(v->i); case VAL_STRING: return make_string(v->s ? v->s : ""); case VAL_FUNCTION: return make_function(v->fn); /* shallow pointer for function bytecode */ case VAL_ARRAY: { const Array *a = (const Array*)v->arr; if (!a || a->count <= 0) { return make_array_from_values(NULL, 0); } /* copy items deeply */ Value *tmp = (Value*)malloc(sizeof(Value) * a->count); if (!tmp) return make_nil(); for (int i = 0; i < a->count; ++i) { tmp[i] = deep_copy_value(&a->items[i]); } Value out = make_array_from_values(tmp, a->count); /* free temporaries (we created new deep copies already moved into out) */ for (int i = 0; i < a->count; ++i) { free_value(tmp[i]); } free(tmp); return out; } case VAL_NIL: default: return make_nil(); } } void free_value(Value v) { if (v.type == VAL_STRING && v.s) { free(v.s); } else if (v.type == VAL_ARRAY && v.arr) { Array *a = (Array*)v.arr; if (--a->refcount == 0) { for (int i = 0; i < a->count; ++i) { free_value(a->items[i]); } free(a->items); free(a); } } /* VAL_FUNCTION: we *do not* free the Bytecode here (caller frees it) */ } void print_value(const Value *v) { switch (v->type) { case VAL_INT: printf("%" PRId64, v->i); break; case VAL_STRING: printf("%s", v->s ? v->s : ""); break; case VAL_FUNCTION: printf("", (void*)v->fn); break; case VAL_ARRAY: { const Array *a = (const Array*)v->arr; printf("["); if (a) { for (int i = 0; i < a->count; ++i) { if (i > 0) printf(", "); print_value(&a->items[i]); } } printf("]"); break; } case VAL_NIL: default: printf("nil"); break; } } int value_is_truthy(const Value *v) { switch (v->type) { case VAL_INT: return v->i != 0; case VAL_STRING: return v->s && v->s[0] != '\0'; case VAL_FUNCTION: return 1; case VAL_ARRAY: { const Array *a = (const Array*)v->arr; return a && a->count > 0; } case VAL_NIL: default: return 0; } } /* allocate a printable C string for the value; caller must free */ char *value_to_string_alloc(const Value *v) { if (!v) return strdup("nil"); char buf[128]; switch (v->type) { case VAL_INT: { char tmp[64]; snprintf(tmp, sizeof(tmp), "%" PRId64, v->i); return strdup(tmp); } case VAL_STRING: return strdup(v->s ? v->s : ""); case VAL_FUNCTION: { snprintf(buf, sizeof(buf), "", (void*)v->fn); return strdup(buf); } case VAL_ARRAY: { int n = array_length(v); if (n < 0) n = 0; snprintf(buf, sizeof(buf), "[array n=%d]", n); return strdup(buf); } case VAL_NIL: default: return strdup("nil"); } } int value_equals(const Value *a, const Value *b) { if (a->type != b->type) return 0; switch (a->type) { case VAL_INT: return a->i == b->i; case VAL_STRING: { const char *sa = a->s ? a->s : ""; const char *sb = b->s ? b->s : ""; return strcmp(sa, sb) == 0; } default: return 0; } } int array_contains(const Value *v, const Value *needle) { if (!v || v->type != VAL_ARRAY || !v->arr) return 0; const Array *a = (const Array*)v->arr; for (int i = 0; i < a->count; ++i) { if (value_equals(&a->items[i], needle)) return 1; } return 0; } int array_index_of(const Value *v, const Value *needle) { if (!v || v->type != VAL_ARRAY || !v->arr) return -1; const Array *a = (const Array*)v->arr; for (int i = 0; i < a->count; ++i) { if (value_equals(&a->items[i], needle)) return i; } return -1; } void array_clear(Value *v) { if (!v || v->type != VAL_ARRAY || !v->arr) return; Array *a = (Array*)v->arr; for (int i = 0; i < a->count; ++i) { free_value(a->items[i]); } a->count = 0; } char *string_substr(const char *s, int start, int len) { if (!s) return strdup(""); int n = (int)strlen(s); if (start < 0) start = 0; if (start > n) start = n; if (len < 0) len = 0; if (start + len > n) len = n - start; char *out = (char*)malloc((size_t)len + 1); if (!out) return strdup(""); memcpy(out, s + start, (size_t)len); out[len] = '\0'; return out; } int string_find(const char *hay, const char *needle) { if (!hay || !needle) return -1; const char *p = strstr(hay, needle); if (!p) return -1; return (int)(p - hay); } Value string_split_to_array(const char *s, const char *sep) { if (!s) s = ""; if (!sep) sep = ""; int seplen = (int)strlen(sep); if (seplen == 0) { /* split into characters */ int n = (int)strlen(s); if (n <= 0) return make_array_from_values(NULL, 0); Value *tmp = (Value*)malloc(sizeof(Value) * n); if (!tmp) return make_array_from_values(NULL, 0); for (int i = 0; i < n; ++i) { char ch[2] = { s[i], 0 }; tmp[i] = make_string(ch); } Value arr = make_array_from_values(tmp, n); for (int i = 0; i < n; ++i) free_value(tmp[i]); free(tmp); return arr; } /* split by separator */ Value *parts = NULL; int count = 0; int cap = 0; const char *cur = s; const char *pos = NULL; while ((pos = strstr(cur, sep)) != NULL) { int len = (int)(pos - cur); char *piece = (char*)malloc((size_t)len + 1); if (!piece) break; memcpy(piece, cur, (size_t)len); piece[len] = '\0'; if (count >= cap) { cap = cap == 0 ? 4 : cap * 2; parts = (Value*)realloc(parts, sizeof(Value) * cap); } parts[count++] = make_string(piece); free(piece); cur = pos + seplen; } /* tail */ char *tail = strdup(cur ? cur : ""); if (count >= cap) { cap = cap == 0 ? 1 : cap + 1; parts = (Value*)realloc(parts, sizeof(Value) * cap); } parts[count++] = make_string(tail ? tail : ""); free(tail); Value arr = make_array_from_values(parts, count); for (int i = 0; i < count; ++i) free_value(parts[i]); free(parts); return arr; } char *array_join_with_sep(const Value *v, const char *sep) { if (!v || v->type != VAL_ARRAY || !v->arr) return strdup(""); if (!sep) sep = ""; const Array *a = (const Array*)v->arr; /* compute total length */ size_t total = 0; int n = a ? a->count : 0; char **parts = (char**)malloc(sizeof(char*) * (n > 0 ? n : 1)); if (!parts) return strdup(""); for (int i = 0; i < n; ++i) { parts[i] = value_to_string_alloc(&a->items[i]); total += strlen(parts[i]); if (i + 1 < n) total += strlen(sep); } char *out = (char*)malloc(total + 1); if (!out) { for (int i = 0; i < n; ++i) free(parts[i]); free(parts); return strdup(""); } size_t off = 0; for (int i = 0; i < n; ++i) { size_t li = strlen(parts[i]); memcpy(out + off, parts[i], li); off += li; if (i + 1 < n) { size_t ls = strlen(sep); memcpy(out + off, sep, ls); off += ls; } free(parts[i]); } free(parts); out[off] = '\0'; return out; }