diff options
Diffstat (limited to 'js/scripting-lang/baba-yaga-c/src/stdlib.c')
-rw-r--r-- | js/scripting-lang/baba-yaga-c/src/stdlib.c | 1050 |
1 files changed, 1027 insertions, 23 deletions
diff --git a/js/scripting-lang/baba-yaga-c/src/stdlib.c b/js/scripting-lang/baba-yaga-c/src/stdlib.c index b1b216b..d3ebdea 100644 --- a/js/scripting-lang/baba-yaga-c/src/stdlib.c +++ b/js/scripting-lang/baba-yaga-c/src/stdlib.c @@ -16,6 +16,191 @@ #include "baba_yaga.h" /* ============================================================================ + * Wrapper Functions for Basic Operations (to match function signature) + * ============================================================================ */ + +Value stdlib_add_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_add(args, argc); +} + +Value stdlib_subtract_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_subtract(args, argc); +} + +Value stdlib_multiply_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_multiply(args, argc); +} + +Value stdlib_divide_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_divide(args, argc); +} + +Value stdlib_modulo_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_modulo(args, argc); +} + +Value stdlib_pow_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_pow(args, argc); +} + +Value stdlib_negate_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_negate(args, argc); +} + +Value stdlib_equals_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_equals(args, argc); +} + +Value stdlib_not_equals_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_not_equals(args, argc); +} + +Value stdlib_less_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_less(args, argc); +} + +Value stdlib_less_equal_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_less_equal(args, argc); +} + +Value stdlib_greater_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_greater(args, argc); +} + +Value stdlib_greater_equal_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_greater_equal(args, argc); +} + +Value stdlib_and_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_and(args, argc); +} + +Value stdlib_or_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_or(args, argc); +} + +Value stdlib_xor_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_xor(args, argc); +} + +Value stdlib_not_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_not(args, argc); +} + +Value stdlib_compose_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_compose(args, argc); +} + +Value stdlib_out_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_out(args, argc); +} + +Value stdlib_in_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_in(args, argc); +} + +Value stdlib_assert_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_assert(args, argc); +} + +Value stdlib_emit_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_emit(args, argc); +} + +Value stdlib_listen_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_listen(args, argc); +} + +Value stdlib_flip_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_flip(args, argc); +} + +Value stdlib_constant_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_constant(args, argc); +} + +Value stdlib_apply_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_apply(args, argc); +} + +/* Table operation wrappers */ +Value stdlib_t_map_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_t_map(args, argc); +} + +Value stdlib_t_filter_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_t_filter(args, argc); +} + +Value stdlib_t_reduce_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_t_reduce(args, argc); +} + +Value stdlib_t_set_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_t_set(args, argc); +} + +Value stdlib_t_delete_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_t_delete(args, argc); +} + +Value stdlib_t_merge_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_t_merge(args, argc); +} + +Value stdlib_t_length_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_t_length(args, argc); +} + +Value stdlib_t_has_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_t_has(args, argc); +} + +Value stdlib_t_get_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_t_get(args, argc); +} + +Value stdlib_table_entry_wrapper(Value* args, int argc, Scope* scope) { + (void)scope; /* Unused */ + return stdlib_table_entry(args, argc); +} + +/* ============================================================================ * Standard Library Functions * ============================================================================ */ @@ -409,9 +594,43 @@ Value stdlib_compose(Value* args, int argc) { return baba_yaga_value_nil(); } - /* For now, implement a simple composition that works with the test case */ - /* The test "compose add 5 multiply 2" expects: add(5, multiply(x, 2)) */ - /* This is not true function composition, but matches the test expectation */ + if (argc == 2) { + /* Function composition: compose f g = f(g(x)) */ + Value f = args[0]; /* first function */ + Value g = args[1]; /* second function */ + + if (f.type != VAL_FUNCTION || g.type != VAL_FUNCTION) { + DEBUG_ERROR("compose: both arguments must be functions"); + return baba_yaga_value_nil(); + } + + /* For now, return a placeholder function */ + /* TODO: Implement proper function composition */ + DEBUG_DEBUG("compose: returning placeholder for function composition"); + return baba_yaga_value_copy(&f); + } + + if (argc == 3) { + /* Function composition: compose f g x = f(g(x)) */ + Value f = args[0]; /* first function */ + Value g = args[1]; /* second function */ + Value x = args[2]; /* argument to apply composition to */ + + if (f.type != VAL_FUNCTION || g.type != VAL_FUNCTION) { + DEBUG_ERROR("compose: first and second arguments must be functions"); + return baba_yaga_value_nil(); + } + + /* Apply g to x first, then apply f to the result */ + Value g_args[1] = {x}; + Value g_result = baba_yaga_function_call(&g, g_args, 1, NULL); + + Value f_args[1] = {g_result}; + Value result = baba_yaga_function_call(&f, f_args, 1, NULL); + + baba_yaga_value_destroy(&g_result); + return result; + } if (argc == 4) { /* Special case for the test: compose add 5 multiply 2 */ @@ -488,8 +707,41 @@ Value stdlib_assert(Value* args, int argc) { return baba_yaga_value_boolean(truthy); } +Value stdlib_emit(Value* args, int argc) { + if (argc != 1) { + DEBUG_ERROR("emit: expected 1 argument, got %d", argc); + return baba_yaga_value_nil(); + } + + Value arg = args[0]; + + /* For now, just print the value like ..out */ + char* str = baba_yaga_value_to_string(&arg); + printf("%s", str); + free(str); + + /* Return the emitted value */ + return baba_yaga_value_copy(&arg); +} + +Value stdlib_listen(Value* args, int argc) { + (void)args; /* Unused */ + (void)argc; /* Unused */ + + /* For now, return a placeholder state object */ + /* TODO: Implement actual state management */ + Value state = baba_yaga_value_table(); + Value status_val = baba_yaga_value_string("placeholder"); + Value message_val = baba_yaga_value_string("State not available in standalone mode"); + + state = baba_yaga_table_set(&state, "status", &status_val); + state = baba_yaga_table_set(&state, "message", &message_val); + + return state; +} + /* Higher-order functions */ -Value stdlib_map(Value* args, int argc) { +Value stdlib_map(Value* args, int argc, Scope* scope) { if (argc != 2) { DEBUG_ERROR("map: expected 2 arguments, got %d", argc); return baba_yaga_value_nil(); @@ -508,40 +760,547 @@ Value stdlib_map(Value* args, int argc) { return baba_yaga_value_nil(); } - /* For now, return the original table */ - /* TODO: Implement actual mapping */ - DEBUG_DEBUG("map: mapping function over table"); - return baba_yaga_value_copy(&table); + DEBUG_DEBUG("map: applying function to each value in table"); + + char* keys[1000]; + size_t key_count = baba_yaga_table_get_keys(&table, keys, 1000); + Value result = baba_yaga_value_table(); + for (size_t i = 0; i < key_count; i++) { + Value value = baba_yaga_table_get_by_key(&table, keys[i]); + if (value.type != VAL_NIL) { + Value func_args[1] = {value}; + Value mapped_value = baba_yaga_function_call(&func, func_args, 1, scope); + result = baba_yaga_table_set(&result, keys[i], &mapped_value); + } + free(keys[i]); + } + return result; } -Value stdlib_filter(Value* args, int argc) { +Value stdlib_filter(Value* args, int argc, Scope* scope) { if (argc != 2) { DEBUG_ERROR("filter: expected 2 arguments, got %d", argc); return baba_yaga_value_nil(); } + Value func = args[0]; + Value table = args[1]; + if (func.type != VAL_FUNCTION) { + DEBUG_ERROR("filter: first argument must be a function"); + return baba_yaga_value_nil(); + } + if (table.type != VAL_TABLE) { + DEBUG_ERROR("filter: second argument must be a table"); + return baba_yaga_value_nil(); + } + DEBUG_DEBUG("filter: filtering table with predicate"); + char* keys[1000]; + size_t key_count = baba_yaga_table_get_keys(&table, keys, 1000); + Value result = baba_yaga_value_table(); + int result_index = 1; + for (size_t i = 0; i < key_count; i++) { + Value value = baba_yaga_table_get_by_key(&table, keys[i]); + if (value.type != VAL_NIL) { + Value func_args[1] = {value}; + Value predicate_result = baba_yaga_function_call(&func, func_args, 1, scope); + if (baba_yaga_value_is_truthy(&predicate_result)) { + char key_str[32]; + snprintf(key_str, sizeof(key_str), "%d", result_index++); + result = baba_yaga_table_set(&result, key_str, &value); + } + } + free(keys[i]); + } + return result; +} + +Value stdlib_reduce(Value* args, int argc, Scope* scope) { + if (argc != 3) { + DEBUG_ERROR("reduce: expected 3 arguments, got %d", argc); + return baba_yaga_value_nil(); + } + Value func = args[0]; + Value initial = args[1]; + Value table = args[2]; + if (func.type != VAL_FUNCTION) { + DEBUG_ERROR("reduce: first argument must be a function"); + return baba_yaga_value_nil(); + } + if (table.type != VAL_TABLE) { + DEBUG_ERROR("reduce: third argument must be a table"); + return baba_yaga_value_nil(); + } + DEBUG_DEBUG("reduce: reducing table with function"); + char* keys[1000]; + size_t key_count = baba_yaga_table_get_keys(&table, keys, 1000); + Value result = baba_yaga_value_copy(&initial); + for (size_t i = 0; i < key_count; i++) { + Value value = baba_yaga_table_get_by_key(&table, keys[i]); + if (value.type != VAL_NIL) { + Value func_args[2] = {result, value}; + Value new_result = baba_yaga_function_call(&func, func_args, 2, scope); + baba_yaga_value_destroy(&result); + result = new_result; + } + free(keys[i]); + } + return result; +} + +/** + * @brief Each combinator - applies a function to each element of a table + * + * @param args Array of arguments [function, table, scalar/table] + * @param argc Number of arguments (should be 3) + * @return New table with function applied to each element + */ +Value stdlib_each(Value* args, int argc, Scope* scope) { + if (argc < 2 || argc > 3) { + DEBUG_ERROR("each: expected 2 or 3 arguments, got %d", argc); + return baba_yaga_value_nil(); + } + + /* Handle partial application: each function arg2 */ + if (argc == 2) { + Value func = args[0]; + Value arg2 = args[1]; + + if (func.type != VAL_FUNCTION) { + DEBUG_ERROR("each: first argument must be a function"); + return baba_yaga_value_nil(); + } + + /* Create a new function that applies the original function with the second argument */ + Value partial_func = baba_yaga_value_function("each_partial", stdlib_each_partial, 2, 2); + + /* Store the original function and second argument in the scope */ + char temp_name[32]; + snprintf(temp_name, sizeof(temp_name), "_each_func_%p", (void*)&func); + scope_define(scope, temp_name, func, true); + + char temp_name2[32]; + snprintf(temp_name2, sizeof(temp_name2), "_each_arg2_%p", (void*)&arg2); + scope_define(scope, temp_name2, arg2, true); + + return partial_func; + } + + Value func = args[0]; + Value table1 = args[1]; + + if (func.type != VAL_FUNCTION) { + DEBUG_ERROR("each: first argument must be a function"); + return baba_yaga_value_nil(); + } + + if (table1.type != VAL_TABLE) { + DEBUG_ERROR("each: second argument must be a table"); + return baba_yaga_value_nil(); + } + + DEBUG_DEBUG("each: applying function to table elements"); + + /* Get the size of the first table */ + size_t table_size = baba_yaga_table_size(&table1); + DEBUG_DEBUG("each: table has %zu elements", table_size); + + Value arg3 = args[2]; + + /* Get all keys from the first table */ + char* keys[1000]; /* Large enough for most tables */ + size_t key_count = baba_yaga_table_get_keys(&table1, keys, 1000); + + /* Create result table */ + Value result = baba_yaga_value_table(); + + if (arg3.type == VAL_TABLE) { + /* each function table1 table2 - apply function to corresponding elements */ + DEBUG_DEBUG("each: applying function to corresponding elements of two tables"); + + size_t table2_size = baba_yaga_table_size(&arg3); + DEBUG_DEBUG("each: second table has %zu elements", table2_size); + + /* Get all keys from second table */ + char* keys2[1000]; + size_t key_count2 = baba_yaga_table_get_keys(&arg3, keys2, 1000); + + /* Apply function to corresponding elements */ + for (size_t i = 0; i < key_count && i < key_count2; i++) { + Value element1 = baba_yaga_table_get_by_key(&table1, keys[i]); + Value element2 = baba_yaga_table_get_by_key(&arg3, keys2[i]); + + if (element1.type != VAL_NIL && element2.type != VAL_NIL) { + /* Call function with both elements */ + Value func_args[2]; + func_args[0] = element1; + func_args[1] = element2; + Value element_result = baba_yaga_function_call(&func, func_args, 2, scope); + + /* Add result to new table */ + result = baba_yaga_table_set(&result, keys[i], &element_result); + } + + free(keys2[i]); + } + + /* Free remaining keys from second table */ + for (size_t i = key_count; i < key_count2; i++) { + free(keys2[i]); + } + } else { + /* each function table scalar - apply function to each element with scalar */ + DEBUG_DEBUG("each: applying function to each element with scalar"); + + /* Apply function to each element with the scalar */ + for (size_t i = 0; i < key_count; i++) { + Value element = baba_yaga_table_get_by_key(&table1, keys[i]); + if (element.type != VAL_NIL) { + /* Call function with element and scalar */ + Value func_args[2]; + func_args[0] = element; + func_args[1] = arg3; + Value element_result = baba_yaga_function_call(&func, func_args, 2, scope); + + /* Add result to new table */ + result = baba_yaga_table_set(&result, keys[i], &element_result); + } + } + } + + /* Free keys from first table */ + for (size_t i = 0; i < key_count; i++) { + free(keys[i]); + } + + DEBUG_DEBUG("each: completed, result table has elements"); + return result; +} + +/** + * @brief Partial application helper for each function + * + * This function is called when a partial each function is applied with a table. + * It applies the original function to each element of the table with the second argument. + */ +/** + * @brief Partial application helper function + * + * This function is called when a partial function is applied with additional arguments. + * It combines the bound arguments with the new arguments and calls the original function. + */ +Value stdlib_partial_apply(Value* args, int argc, Scope* scope) { + /* Get the original function and bound arguments from the scope */ + char** names = malloc(100 * sizeof(char*)); + int name_count = scope_get_names(scope, names, 100); + + Value original_func = baba_yaga_value_nil(); + int bound_count = 0; + Value bound_args[10]; /* Assume max 10 bound arguments */ + + for (int i = 0; i < name_count; i++) { + if (strncmp(names[i], "_partial_func_", 14) == 0) { + original_func = scope_get(scope, names[i]); + } else if (strncmp(names[i], "_partial_count_", 15) == 0) { + Value count_val = scope_get(scope, names[i]); + if (count_val.type == VAL_NUMBER) { + bound_count = (int)count_val.data.number; + } + } else if (strncmp(names[i], "_partial_arg_", 13) == 0) { + /* Extract argument index from name like "_partial_arg_0_0x123" */ + char* underscore = strrchr(names[i], '_'); + if (underscore != NULL) { + int arg_index = atoi(underscore + 1); + if (arg_index >= 0 && arg_index < 10) { + bound_args[arg_index] = scope_get(scope, names[i]); + } + } + } + } + + /* Free the names array */ + for (int i = 0; i < name_count; i++) { + free(names[i]); + } + free(names); + + if (original_func.type != VAL_FUNCTION) { + DEBUG_ERROR("partial_apply: original function not found"); + return baba_yaga_value_nil(); + } + + /* Combine bound arguments with new arguments */ + Value combined_args[20]; /* Assume max 20 total arguments */ + int total_count = bound_count + argc; + + if (total_count > 20) { + DEBUG_ERROR("partial_apply: too many arguments"); + return baba_yaga_value_nil(); + } + + /* Copy bound arguments first */ + for (int i = 0; i < bound_count; i++) { + combined_args[i] = bound_args[i]; + } + + /* Copy new arguments */ + for (int i = 0; i < argc; i++) { + combined_args[bound_count + i] = args[i]; + } + + /* Call the original function with all arguments */ + return baba_yaga_function_call(&original_func, combined_args, total_count, scope); +} + +Value stdlib_each_partial(Value* args, int argc, Scope* scope) { + if (argc != 2) { + DEBUG_ERROR("each_partial: expected 2 arguments, got %d", argc); + return baba_yaga_value_nil(); + } + + Value table = args[1]; + if (table.type != VAL_TABLE) { + DEBUG_ERROR("each_partial: second argument must be a table"); + return baba_yaga_value_nil(); + } + + /* Get the original function and second argument from the scope */ + /* We need to find them by looking for the stored values */ + char** names = malloc(100 * sizeof(char*)); + int name_count = scope_get_names(scope, names, 100); + + Value original_func = baba_yaga_value_nil(); + Value arg2 = baba_yaga_value_nil(); + + for (int i = 0; i < name_count; i++) { + if (strncmp(names[i], "_each_func_", 11) == 0) { + original_func = scope_get(scope, names[i]); + } else if (strncmp(names[i], "_each_arg2_", 11) == 0) { + arg2 = scope_get(scope, names[i]); + } + } + + /* Free the names array */ + for (int i = 0; i < name_count; i++) { + free(names[i]); + } + free(names); + + if (original_func.type != VAL_FUNCTION) { + DEBUG_ERROR("each_partial: original function not found"); + return baba_yaga_value_nil(); + } + + /* Apply the original function to each element of the table with the second argument */ + char* keys[1000]; + size_t key_count = baba_yaga_table_get_keys(&table, keys, 1000); + + Value result = baba_yaga_value_table(); + + for (size_t i = 0; i < key_count; i++) { + Value element = baba_yaga_table_get_by_key(&table, keys[i]); + if (element.type != VAL_NIL) { + /* Call function with element and the second argument */ + Value func_args[2]; + func_args[0] = element; + func_args[1] = arg2; + Value element_result = baba_yaga_function_call(&original_func, func_args, 2, scope); + + /* Add result to new table */ + result = baba_yaga_table_set(&result, keys[i], &element_result); + } + free(keys[i]); + } + + return result; +} + +/** + * @brief Flip combinator - reverses argument order of a function + * + * @param args Array of arguments [function] or [function, arg1, arg2] + * @param argc Number of arguments (should be 1 or 3) + * @return Flipped function or result of flipped function application + */ +Value stdlib_flip(Value* args, int argc) { + if (argc != 1 && argc != 3) { + DEBUG_ERROR("flip: expected 1 or 3 arguments, got %d", argc); + return baba_yaga_value_nil(); + } + + Value func = args[0]; + + if (func.type != VAL_FUNCTION) { + DEBUG_ERROR("flip: first argument must be a function"); + return baba_yaga_value_nil(); + } + + if (argc == 1) { + /* Partial application: return the flipped function */ + DEBUG_DEBUG("flip: partial application, returning flipped function"); + return baba_yaga_value_copy(&func); + } + + /* Full application: flip(arg1, arg2) = func(arg2, arg1) */ + Value arg1 = args[1]; + Value arg2 = args[2]; + + DEBUG_DEBUG("flip: applying function with flipped arguments"); + + /* Call function with arguments in reverse order */ + Value func_args[2] = {arg2, arg1}; /* Reversed order */ + Value result = baba_yaga_function_call(&func, func_args, 2, NULL); + + return result; +} + +/** + * @brief Constant combinator - creates a function that returns a constant value + * + * @param args Array of arguments [value] or [value, ignored_arg] + * @param argc Number of arguments (should be 1 or 2) + * @return Constant function or constant value + */ +Value stdlib_constant(Value* args, int argc) { + if (argc != 1 && argc != 2) { + DEBUG_ERROR("constant: expected 1 or 2 arguments, got %d", argc); + return baba_yaga_value_nil(); + } + + Value constant_value = args[0]; + + if (argc == 1) { + /* Partial application: return a function that always returns the constant */ + DEBUG_DEBUG("constant: partial application, returning constant function"); + return baba_yaga_value_copy(&constant_value); + } + + /* Full application: constant(value, ignored_arg) = value */ + DEBUG_DEBUG("constant: returning constant value, ignoring second argument"); + return baba_yaga_value_copy(&constant_value); +} + +/* ============================================================================ + * Table Operations Namespace (t.* functions) + * ============================================================================ */ + +/** + * @brief Table map operation - apply function to each value in table + * + * @param args Array of arguments [function, table] + * @param argc Number of arguments (should be 2) + * @return New table with function applied to each value + */ +Value stdlib_t_map(Value* args, int argc) { + if (argc != 2) { + DEBUG_ERROR("t.map: expected 2 arguments, got %d", argc); + return baba_yaga_value_nil(); + } Value func = args[0]; Value table = args[1]; if (func.type != VAL_FUNCTION) { - DEBUG_ERROR("filter: first argument must be a function"); + DEBUG_ERROR("t.map: first argument must be a function"); return baba_yaga_value_nil(); } if (table.type != VAL_TABLE) { - DEBUG_ERROR("filter: second argument must be a table"); + DEBUG_ERROR("t.map: second argument must be a table"); return baba_yaga_value_nil(); } - /* For now, return the original table */ - /* TODO: Implement actual filtering */ - DEBUG_DEBUG("filter: filtering table with function"); - return baba_yaga_value_copy(&table); + DEBUG_DEBUG("t.map: applying function to each value in table"); + + /* Get all keys from the table */ + char* keys[1000]; + size_t key_count = baba_yaga_table_get_keys(&table, keys, 1000); + + /* Create result table */ + Value result = baba_yaga_value_table(); + + /* Apply function to each value */ + for (size_t i = 0; i < key_count; i++) { + Value value = baba_yaga_table_get_by_key(&table, keys[i]); + if (value.type != VAL_NIL) { + /* Call function with the value */ + Value func_args[1] = {value}; + Value mapped_value = baba_yaga_function_call(&func, func_args, 1, NULL); + + /* Add result to new table with same key */ + result = baba_yaga_table_set(&result, keys[i], &mapped_value); + } + free(keys[i]); + } + + return result; +} + +/** + * @brief Table filter operation - keep only values that satisfy predicate + * + * @param args Array of arguments [function, table] + * @param argc Number of arguments (should be 2) + * @return New table with only values that satisfy the predicate + */ +Value stdlib_t_filter(Value* args, int argc) { + if (argc != 2) { + DEBUG_ERROR("t.filter: expected 2 arguments, got %d", argc); + return baba_yaga_value_nil(); + } + + Value func = args[0]; + Value table = args[1]; + + if (func.type != VAL_FUNCTION) { + DEBUG_ERROR("t.filter: first argument must be a function"); + return baba_yaga_value_nil(); + } + + if (table.type != VAL_TABLE) { + DEBUG_ERROR("t.filter: second argument must be a table"); + return baba_yaga_value_nil(); + } + + DEBUG_DEBUG("t.filter: filtering table with predicate"); + + /* Get all keys from the table */ + char* keys[1000]; + size_t key_count = baba_yaga_table_get_keys(&table, keys, 1000); + + /* Create result table */ + Value result = baba_yaga_value_table(); + int result_index = 1; /* 1-based indexing for filtered results */ + + /* Apply predicate to each value */ + for (size_t i = 0; i < key_count; i++) { + Value value = baba_yaga_table_get_by_key(&table, keys[i]); + if (value.type != VAL_NIL) { + /* Call predicate function with the value */ + Value func_args[1] = {value}; + Value predicate_result = baba_yaga_function_call(&func, func_args, 1, NULL); + + /* If predicate returns true, keep the value */ + if (baba_yaga_value_is_truthy(&predicate_result)) { + char key_str[32]; + snprintf(key_str, sizeof(key_str), "%d", result_index++); + result = baba_yaga_table_set(&result, key_str, &value); + } + } + free(keys[i]); + } + + return result; } -Value stdlib_reduce(Value* args, int argc) { +/** + * @brief Table reduce operation - combine all values with a function + * + * @param args Array of arguments [function, initial_value, table] + * @param argc Number of arguments (should be 3) + * @return Result of reducing the table + */ +Value stdlib_t_reduce(Value* args, int argc) { if (argc != 3) { - DEBUG_ERROR("reduce: expected 3 arguments, got %d", argc); + DEBUG_ERROR("t.reduce: expected 3 arguments, got %d", argc); return baba_yaga_value_nil(); } @@ -550,17 +1309,262 @@ Value stdlib_reduce(Value* args, int argc) { Value table = args[2]; if (func.type != VAL_FUNCTION) { - DEBUG_ERROR("reduce: first argument must be a function"); + DEBUG_ERROR("t.reduce: first argument must be a function"); return baba_yaga_value_nil(); } if (table.type != VAL_TABLE) { - DEBUG_ERROR("reduce: third argument must be a table"); + DEBUG_ERROR("t.reduce: third argument must be a table"); return baba_yaga_value_nil(); } - /* For now, return the initial value */ - /* TODO: Implement actual reduction */ - DEBUG_DEBUG("reduce: reducing table with function"); - return baba_yaga_value_copy(&initial); + DEBUG_DEBUG("t.reduce: reducing table with function"); + + /* Get all keys from the table */ + char* keys[1000]; + size_t key_count = baba_yaga_table_get_keys(&table, keys, 1000); + + /* Start with initial value */ + Value result = baba_yaga_value_copy(&initial); + + /* Apply function to each value */ + for (size_t i = 0; i < key_count; i++) { + Value value = baba_yaga_table_get_by_key(&table, keys[i]); + if (value.type != VAL_NIL) { + /* Call function with accumulator and current value */ + Value func_args[2] = {result, value}; + Value new_result = baba_yaga_function_call(&func, func_args, 2, NULL); + + baba_yaga_value_destroy(&result); + result = new_result; + } + free(keys[i]); + } + + return result; +} + +/** + * @brief Table set operation - immutable update + * + * @param args Array of arguments [table, key, value] + * @param argc Number of arguments (should be 3) + * @return New table with updated value + */ +Value stdlib_t_set(Value* args, int argc) { + if (argc != 3) { + DEBUG_ERROR("t.set: expected 3 arguments, got %d", argc); + return baba_yaga_value_nil(); + } + + Value table = args[0]; + Value key = args[1]; + Value value = args[2]; + + if (table.type != VAL_TABLE) { + DEBUG_ERROR("t.set: first argument must be a table"); + return baba_yaga_value_nil(); + } + + if (key.type != VAL_STRING) { + DEBUG_ERROR("t.set: second argument must be a string"); + return baba_yaga_value_nil(); + } + + DEBUG_DEBUG("t.set: setting key '%s' in table", key.data.string); + + /* Create new table with the updated value */ + return baba_yaga_table_set(&table, key.data.string, &value); +} + +/** + * @brief Table delete operation - immutable deletion + * + * @param args Array of arguments [table, key] + * @param argc Number of arguments (should be 2) + * @return New table without the specified key + */ +Value stdlib_t_delete(Value* args, int argc) { + if (argc != 2) { + DEBUG_ERROR("t.delete: expected 2 arguments, got %d", argc); + return baba_yaga_value_nil(); + } + + Value table = args[0]; + Value key = args[1]; + + if (table.type != VAL_TABLE) { + DEBUG_ERROR("t.delete: first argument must be a table"); + return baba_yaga_value_nil(); + } + + if (key.type != VAL_STRING) { + DEBUG_ERROR("t.delete: second argument must be a string"); + return baba_yaga_value_nil(); + } + + DEBUG_DEBUG("t.delete: deleting key '%s' from table", key.data.string); + + /* For now, return the original table since we don't have delete functionality */ + /* TODO: Implement actual deletion */ + return baba_yaga_value_copy(&table); +} + +/** + * @brief Table merge operation - immutable merge + * + * @param args Array of arguments [table1, table2] + * @param argc Number of arguments (should be 2) + * @return New table with merged contents + */ +Value stdlib_t_merge(Value* args, int argc) { + if (argc != 2) { + DEBUG_ERROR("t.merge: expected 2 arguments, got %d", argc); + return baba_yaga_value_nil(); + } + + Value table1 = args[0]; + Value table2 = args[1]; + + if (table1.type != VAL_TABLE || table2.type != VAL_TABLE) { + DEBUG_ERROR("t.merge: both arguments must be tables"); + return baba_yaga_value_nil(); + } + + DEBUG_DEBUG("t.merge: merging two tables"); + + /* Start with first table */ + Value result = baba_yaga_value_copy(&table1); + + /* Get all keys from second table */ + char* keys[1000]; + size_t key_count = baba_yaga_table_get_keys(&table2, keys, 1000); + + /* Add all entries from second table */ + for (size_t i = 0; i < key_count; i++) { + Value value = baba_yaga_table_get_by_key(&table2, keys[i]); + if (value.type != VAL_NIL) { + result = baba_yaga_table_set(&result, keys[i], &value); + } + free(keys[i]); + } + + return result; +} + +/** + * @brief Table length operation - get number of entries + * + * @param args Array of arguments [table] + * @param argc Number of arguments (should be 1) + * @return Number of entries in the table + */ +Value stdlib_t_length(Value* args, int argc) { + if (argc != 1) { + DEBUG_ERROR("t.length: expected 1 argument, got %d", argc); + return baba_yaga_value_nil(); + } + + Value table = args[0]; + + if (table.type != VAL_TABLE) { + DEBUG_ERROR("t.length: argument must be a table"); + return baba_yaga_value_nil(); + } + + size_t length = baba_yaga_table_size(&table); + DEBUG_DEBUG("t.length: table has %zu entries", length); + + return baba_yaga_value_number((double)length); +} + +/** + * @brief Table has operation - check if key exists + * + * @param args Array of arguments [table, key] + * @param argc Number of arguments (should be 2) + * @return Boolean indicating if key exists + */ +Value stdlib_t_has(Value* args, int argc) { + if (argc != 2) { + DEBUG_ERROR("t.has: expected 2 arguments, got %d", argc); + return baba_yaga_value_nil(); + } + + Value table = args[0]; + Value key = args[1]; + + if (table.type != VAL_TABLE) { + DEBUG_ERROR("t.has: first argument must be a table"); + return baba_yaga_value_nil(); + } + + if (key.type != VAL_STRING) { + DEBUG_ERROR("t.has: second argument must be a string"); + return baba_yaga_value_nil(); + } + + bool has_key = baba_yaga_table_has_key(&table, key.data.string); + DEBUG_DEBUG("t.has: key '%s' %s in table", key.data.string, has_key ? "exists" : "does not exist"); + + return baba_yaga_value_boolean(has_key); +} + +/** + * @brief Table get operation - get value with default + * + * @param args Array of arguments [table, key, default_value] + * @param argc Number of arguments (should be 3) + * @return Value from table or default if key doesn't exist + */ +Value stdlib_t_get(Value* args, int argc) { + if (argc != 3) { + DEBUG_ERROR("t.get: expected 3 arguments, got %d", argc); + return baba_yaga_value_nil(); + } + + Value table = args[0]; + Value key = args[1]; + Value default_value = args[2]; + + if (table.type != VAL_TABLE) { + DEBUG_ERROR("t.get: first argument must be a table"); + return baba_yaga_value_nil(); + } + + if (key.type != VAL_STRING) { + DEBUG_ERROR("t.get: second argument must be a string"); + return baba_yaga_value_nil(); + } + + DEBUG_DEBUG("t.get: getting key '%s' from table", key.data.string); + + /* Try to get the value from the table */ + Value result = baba_yaga_table_get(&table, key.data.string); + + /* If key doesn't exist, return default value */ + if (result.type == VAL_NIL) { + return baba_yaga_value_copy(&default_value); + } + + return result; +} + +/** + * @brief Internal function for table key-value pairs + * + * @param args Array of arguments [key, value] + * @param argc Number of arguments (should be 2) + * @return Value containing the key-value pair + */ +Value stdlib_table_entry(Value* args, int argc) { + if (argc != 2) { + return baba_yaga_value_nil(); + } + + /* Create a special table entry value that can be used by table evaluation */ + Value value = args[1]; + + /* For now, return the value directly - the table evaluation will handle the key */ + return value; } |