1
0
Fork 0
forked from fun/fun
fun/src/value.c

497 lines
14 KiB
C
Raw Normal View History

2025-09-13 05:09:20 +02:00
#include "value.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
2025-09-15 01:36:20 +02:00
typedef struct Array {
int refcount;
int count;
Value *items; /* owns items; each item owned by array */
} Array;
2025-09-13 05:09:20 +02:00
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;
}
2025-09-15 01:36:20 +02:00
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]);
2025-09-15 01:53:41 +02:00
a->items[index] = newElem; /* take ownership */
2025-09-15 01:36:20 +02:00
return 1;
}
2025-09-15 01:53:41 +02:00
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;
}
2025-09-13 05:09:20 +02:00
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;
2025-09-15 01:36:20 +02:00
case VAL_ARRAY: {
Array *a = (Array*)v->arr;
out.arr = (struct Array*)a;
if (a) a->refcount++;
break;
}
2025-09-13 05:09:20 +02:00
case VAL_NIL:
default:
break;
}
return out;
}
2025-09-15 01:53:41 +02:00
/* 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();
}
}
2025-09-13 05:09:20 +02:00
void free_value(Value v) {
if (v.type == VAL_STRING && v.s) {
free(v.s);
2025-09-15 01:36:20 +02:00
} 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);
}
2025-09-13 05:09:20 +02:00
}
/* 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("<function@%p>", (void*)v->fn);
break;
2025-09-15 01:36:20 +02:00
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;
}
2025-09-13 05:09:20 +02:00
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;
2025-09-15 01:36:20 +02:00
case VAL_ARRAY: {
const Array *a = (const Array*)v->arr;
return a && a->count > 0;
}
2025-09-13 05:09:20 +02:00
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), "<function@%p>", (void*)v->fn);
return strdup(buf);
}
2025-09-15 01:36:20 +02:00
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");
}
}
2025-09-15 03:59:03 +02:00
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;
}