aboutsummaryrefslogtreecommitdiff
path: root/src/value.c
blob: 61d6d4a96ee46e4542324a335d2a4e8cfad09822 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#include "../include/value.h"
#include "../include/std_gc/hash_map.h"
#include <string.h>
#include <stdio.h>
#ifdef DEBUG
#define GC_DEBUG
#endif
#include <gc.h>

static uint64_t hash_range(const uint8_t *data, size_t size) {
    uint64_t result = 0xdec05eba;
    while(size) {
        result = ((result << 5) + result) + *data;
        ++data;
        --size;
    }
    return result;
}

static void tsl_string_to_ref(const TslString *self, TslStringView *result) {
    result->data = self->data;
    result->size = self->size;
}

uint64_t tsl_value_hash(const TslValue *key) {
    switch((TslType)key->type) {
        case TSL_TYPE_NULL:
            return 0;
        case TSL_TYPE_NUMBER:
            return *(uint64_t*)&key->data.number;
        case TSL_TYPE_STRING:
            return hash_range((uint8_t*)key->data.string->data, key->data.string->size);
        case TSL_TYPE_STRING_REF:
            return hash_range((uint8_t*)key->data.string_ref.data, key->data.string_ref.size);
        case TSL_TYPE_BOOL:
            return key->data.boolean;
        case TSL_TYPE_LIST:
            return (uint64_t)key->data.list;
        case TSL_TYPE_MAP:
            return (uint64_t)key->data.map;
        case TSL_TYPE_FUNCTION:
            return key->data.function;
        case TSL_TYPE_USERDATA:
            return (uint64_t)key->data.userdata;
    }
    return 0;
}

int tsl_value_equals(const TslValue *lhs, const TslValue *rhs) {
    if(lhs->type == rhs->type) {
        if(lhs->type == TSL_TYPE_STRING) {
            return lhs->data.string->size == rhs->data.string->size
                && memcmp(lhs->data.string->data, rhs->data.string->data, lhs->data.string->size) == 0;
        } else if(lhs->type == TSL_TYPE_STRING_REF) {
            return lhs->data.string_ref.size == rhs->data.string_ref.size
                && memcmp(lhs->data.string_ref.data, rhs->data.string_ref.data, lhs->data.string_ref.size) == 0;
        } else {
            return *(uint64_t*)&lhs->data == *(uint64_t*)&rhs->data;
        }
    } else if(lhs->type == TSL_TYPE_STRING && rhs->type == TSL_TYPE_STRING_REF) {
        return lhs->data.string->size == rhs->data.string_ref.size
                && memcmp(lhs->data.string->data, rhs->data.string_ref.data, lhs->data.string->size) == 0;
    } else if(lhs->type == TSL_TYPE_STRING_REF && rhs->type == TSL_TYPE_STRING) {
        return lhs->data.string_ref.size == rhs->data.string->size
                && memcmp(lhs->data.string_ref.data, rhs->data.string->data, lhs->data.string_ref.size) == 0;
    } else {
        return 0;
    }
}

void tsl_value_clear(TslValue *self) {
    memset(self, 0, sizeof(TslValue));
}

static int tsl_string_merge(const TslStringView *str1, const TslStringView *str2, TslString **result) {
    size_t new_size = str1->size + str2->size;
    char *new_str_data;
    *result = GC_MALLOC_ATOMIC(sizeof(TslString));
    new_str_data = GC_MALLOC_ATOMIC(new_size + 1);

    if(!*result) {
        GC_FREE(*result);
        GC_FREE(new_str_data);
        fprintf(stderr, "Error: out of memory\n");
        return 0;
    }
    if(!new_str_data) {
        GC_FREE(*result);
        GC_FREE(new_str_data);
        fprintf(stderr, "Error: out of memory\n");
        return 0;
    }

    memcpy(new_str_data, str1->data, str1->size);
    memcpy(new_str_data + str1->size, str2->data, str2->size);
    new_str_data[new_size] = '\0';
    (*result)->data = new_str_data;
    (*result)->size = new_size;

    return 1;
}

int tsl_value_add(const TslValue *lhs_value, const TslValue *rhs_value, TslValue *result) {
    tsl_value_clear(result);
    switch((TslType)lhs_value->type) {
        case TSL_TYPE_NULL:
        case TSL_TYPE_BOOL:
        case TSL_TYPE_MAP:
        case TSL_TYPE_FUNCTION:
        case TSL_TYPE_USERDATA: {
            fprintf(stderr, "Error: Unable to perform operation '+' on a TODO\n");
            return 0;
        }
        case TSL_TYPE_NUMBER: {
            if(rhs_value->type != TSL_TYPE_NUMBER) {
                fprintf(stderr, "Error: Unable to perform operation '+' on a number and a TODO\n");
                return 0;
            }
            result->data.number = lhs_value->data.number + rhs_value->data.number;
            result->type = TSL_TYPE_NUMBER;
            return 1;
        }
        case TSL_TYPE_STRING: {
            TslStringView lhs_str;
            tsl_string_to_ref(lhs_value->data.string, &lhs_str);
            result->type = TSL_TYPE_STRING;
            if(rhs_value->type == TSL_TYPE_STRING) {
                TslStringView rhs_str;
                tsl_string_to_ref(rhs_value->data.string, &rhs_str);
                return tsl_string_merge(&lhs_str, &rhs_str, &result->data.string);
            } else if(rhs_value->type == TSL_TYPE_STRING_REF) {
                return tsl_string_merge(&lhs_str, &rhs_value->data.string_ref, &result->data.string);
            } else {
                fprintf(stderr, "Error: Unable to perform operation '+' on a string and TODO\n");
                return 0;
            }
            result->type = TSL_TYPE_STRING;
            return 1;
        }
        case TSL_TYPE_STRING_REF: {
            result->type = TSL_TYPE_STRING;
            if(rhs_value->type == TSL_TYPE_STRING) {
                TslStringView rhs_str;
                tsl_string_to_ref(rhs_value->data.string, &rhs_str);
                return tsl_string_merge(&lhs_value->data.string_ref, &rhs_str, &result->data.string);
            } else if(rhs_value->type == TSL_TYPE_STRING_REF) {
                return tsl_string_merge(&lhs_value->data.string_ref, &rhs_value->data.string_ref, &result->data.string);
            } else {
                fprintf(stderr, "Error: Unable to perform operation '+' on a string and TODO\n");
                return 0;
            }
            result->type = TSL_TYPE_STRING;
            return 1;
        }
        case TSL_TYPE_LIST: {
            /*
                Create a new list by combining lhs_value list and rhs_value list.
                For example, [1, 2] + [3, 4] becomes [1, 2, 3, 4]
            */
            size_t list_size = sizeof(TslValue) * (lhs_value->data.list->size + rhs_value->data.list->size);

            if(rhs_value->type != TSL_TYPE_LIST) {
                fprintf(stderr, "Error: Unable to perform operation '+' on a list and a TODO\n");
                return 0;
            }
            
            result->data.list = GC_MALLOC(sizeof(TslList));
            if(!result->data.list) {
                fprintf(stderr, "Error: Failed to create list. Reason: out of memory\n");
                return 0;
            }

            tsl_list_set_capacity_hint(result->data.list, list_size);
            result->data.list->size = list_size;

            memcpy((char*)result->data.list->data, lhs_value->data.list->data, lhs_value->data.list->size);
            memcpy((char*)result->data.list->data + lhs_value->data.list->size, rhs_value->data.list->data, rhs_value->data.list->size);

            result->type = TSL_TYPE_LIST;
            return 1;
        }
    }
    return 0;
}

TslValue* tsl_string_ref_get(const TslStringView *self, double index) {
    /* TODO: Verify if overflow check is needed */
    intptr_t index_i = index;
    if(index_i >= 0 && index_i < (intptr_t)self->size / (intptr_t)sizeof(TslValue))
        return (TslValue*)self->data + index_i;
    return NULL;
}