// string_backend.c #include "wren.h" #include #include #include // Helper to validate that the value in a slot is a string. static bool validateString(WrenVM* vm, int slot, const char* name) { if (wrenGetSlotType(vm, slot) == WREN_TYPE_STRING) return true; wrenSetSlotString(vm, 0, "Argument must be a string."); wrenAbortFiber(vm, 0); return false; } // Implements String.endsWith(_). void stringEndsWith(WrenVM* vm) { if (!validateString(vm, 1, "Suffix")) return; int stringLength, suffixLength; const char* string = wrenGetSlotBytes(vm, 0, &stringLength); const char* suffix = wrenGetSlotBytes(vm, 1, &suffixLength); if (suffixLength > stringLength) { wrenSetSlotBool(vm, 0, false); return; } wrenSetSlotBool(vm, 0, memcmp(string + stringLength - suffixLength, suffix, suffixLength) == 0); } // Implements String.startsWith(_). void stringStartsWith(WrenVM* vm) { if (!validateString(vm, 1, "Prefix")) return; int stringLength, prefixLength; const char* string = wrenGetSlotBytes(vm, 0, &stringLength); const char* prefix = wrenGetSlotBytes(vm, 1, &prefixLength); if (prefixLength > stringLength) { wrenSetSlotBool(vm, 0, false); return; } wrenSetSlotBool(vm, 0, memcmp(string, prefix, prefixLength) == 0); } // Implements String.replace(_, _). void stringReplace(WrenVM* vm) { if (!validateString(vm, 1, "From")) return; if (!validateString(vm, 2, "To")) return; int haystackLen, fromLen, toLen; const char* haystack = wrenGetSlotBytes(vm, 0, &haystackLen); const char* from = wrenGetSlotBytes(vm, 1, &fromLen); const char* to = wrenGetSlotBytes(vm, 2, &toLen); if (fromLen == 0) { wrenSetSlotString(vm, 0, haystack); // Nothing to replace. return; } // Allocate a buffer for the result. This is a rough estimate. // A more robust implementation would calculate the exact size or reallocate. size_t resultCapacity = haystackLen * (toLen > fromLen ? toLen / fromLen + 1 : 1) + 1; char* result = (char*)malloc(resultCapacity); if (!result) { // Handle allocation failure wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } char* dest = result; const char* p = haystack; const char* end = haystack + haystackLen; while (p < end) { const char* found = strstr(p, from); if (found) { size_t len = found - p; memcpy(dest, p, len); dest += len; memcpy(dest, to, toLen); dest += toLen; p = found + fromLen; } else { size_t len = end - p; memcpy(dest, p, len); dest += len; p = end; } } *dest = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.split(_). void stringSplit(WrenVM* vm) { if (!validateString(vm, 1, "Delimiter")) return; int haystackLen, delimLen; const char* haystack = wrenGetSlotBytes(vm, 0, &haystackLen); const char* delim = wrenGetSlotBytes(vm, 1, &delimLen); if (delimLen == 0) { wrenSetSlotString(vm, 0, "Delimiter cannot be empty."); wrenAbortFiber(vm, 0); return; } wrenSetSlotNewList(vm, 0); // Create the list to return. const char* p = haystack; const char* end = haystack + haystackLen; while (p < end) { const char* found = strstr(p, delim); if (found) { wrenSetSlotBytes(vm, 1, p, found - p); wrenInsertInList(vm, 0, -1, 1); p = found + delimLen; } else { wrenSetSlotBytes(vm, 1, p, end - p); wrenInsertInList(vm, 0, -1, 1); p = end; } } // If the string ends with the delimiter, add an empty string. if (haystackLen > 0 && (haystackLen >= delimLen) && strcmp(haystack + haystackLen - delimLen, delim) == 0) { wrenSetSlotBytes(vm, 1, "", 0); wrenInsertInList(vm, 0, -1, 1); } } // Binds the foreign methods for the String class. WrenForeignMethodFn bindStringForeignMethod(WrenVM* vm, const char* module, const char* className, bool isStatic, const char* signature) { if (strcmp(module, "main") != 0 && strcmp(module, "core") != 0) return NULL; if (strcmp(className, "String") != 0 || isStatic) return NULL; if (strcmp(signature, "endsWith(_)") == 0) return stringEndsWith; if (strcmp(signature, "startsWith(_)") == 0) return stringStartsWith; if (strcmp(signature, "replace(_,_)") == 0) return stringReplace; if (strcmp(signature, "split(_)") == 0) return stringSplit; return NULL; }