// string_backend.c #include "wren.h" #include #include #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; } // Helper to validate that the value in a slot is a number. static bool validateNumber(WrenVM* vm, int slot, const char* name) { if (wrenGetSlotType(vm, slot) == WREN_TYPE_NUM) return true; wrenSetSlotString(vm, 0, "Argument must be a number."); 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); return; } size_t resultCapacity = haystackLen * (toLen > fromLen ? toLen / fromLen + 1 : 1) + 1; char* result = (char*)malloc(resultCapacity); if (!result) { 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); 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 (haystackLen > 0 && (haystackLen >= delimLen) && memcmp(haystack + haystackLen - delimLen, delim, delimLen) == 0) { wrenSetSlotBytes(vm, 1, "", 0); wrenInsertInList(vm, 0, -1, 1); } } // Implements String.toUpper(). void stringToUpper(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); char* result = (char*)malloc(len + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } for (int i = 0; i < len; i++) { result[i] = toupper((unsigned char)str[i]); } result[len] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.toLower(). void stringToLower(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); char* result = (char*)malloc(len + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } for (int i = 0; i < len; i++) { result[i] = tolower((unsigned char)str[i]); } result[len] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.trim(). void stringTrim(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); int start = 0; int end = len - 1; while (start < len && isspace((unsigned char)str[start])) start++; while (end >= 0 && isspace((unsigned char)str[end])) end--; if (start > end) { wrenSetSlotString(vm, 0, ""); return; } wrenSetSlotBytes(vm, 0, str + start, end - start + 1); } // Implements String.trimStart(). void stringTrimStart(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); int start = 0; while (start < len && isspace((unsigned char)str[start])) start++; wrenSetSlotBytes(vm, 0, str + start, len - start); } // Implements String.trimEnd(). void stringTrimEnd(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); int end = len - 1; while (end >= 0 && isspace((unsigned char)str[end])) end--; wrenSetSlotBytes(vm, 0, str, end + 1); } // Implements String.count(_). void stringCount(WrenVM* vm) { if (!validateString(vm, 1, "Substring")) return; int haystackLen, needleLen; const char* haystack = wrenGetSlotBytes(vm, 0, &haystackLen); const char* needle = wrenGetSlotBytes(vm, 1, &needleLen); if (needleLen == 0) { wrenSetSlotDouble(vm, 0, 0); return; } int count = 0; const char* p = haystack; const char* end = haystack + haystackLen; while (p < end) { const char* found = strstr(p, needle); if (found) { count++; p = found + needleLen; } else { break; } } wrenSetSlotDouble(vm, 0, count); } // Implements String.indexOf(_). void stringIndexOf(WrenVM* vm) { if (!validateString(vm, 1, "Substring")) return; int haystackLen, needleLen; const char* haystack = wrenGetSlotBytes(vm, 0, &haystackLen); const char* needle = wrenGetSlotBytes(vm, 1, &needleLen); if (needleLen == 0) { wrenSetSlotDouble(vm, 0, 0); return; } const char* found = strstr(haystack, needle); if (found) { wrenSetSlotDouble(vm, 0, found - haystack); } else { wrenSetSlotDouble(vm, 0, -1); } } // Implements String.lastIndexOf(_). void stringLastIndexOf(WrenVM* vm) { if (!validateString(vm, 1, "Substring")) return; int haystackLen, needleLen; const char* haystack = wrenGetSlotBytes(vm, 0, &haystackLen); const char* needle = wrenGetSlotBytes(vm, 1, &needleLen); if (needleLen == 0 || needleLen > haystackLen) { wrenSetSlotDouble(vm, 0, -1); return; } const char* lastFound = NULL; const char* p = haystack; while ((p = strstr(p, needle)) != NULL) { lastFound = p; p++; } if (lastFound) { wrenSetSlotDouble(vm, 0, lastFound - haystack); } else { wrenSetSlotDouble(vm, 0, -1); } } // Implements String.contains(_). void stringContains(WrenVM* vm) { if (!validateString(vm, 1, "Substring")) return; int haystackLen, needleLen; const char* haystack = wrenGetSlotBytes(vm, 0, &haystackLen); const char* needle = wrenGetSlotBytes(vm, 1, &needleLen); if (needleLen == 0) { wrenSetSlotBool(vm, 0, true); return; } wrenSetSlotBool(vm, 0, strstr(haystack, needle) != NULL); } // Implements String.toInt(). void stringToInt(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); char* endptr; errno = 0; long value = strtol(str, &endptr, 10); if (errno != 0 || endptr == str || *endptr != '\0') { wrenSetSlotNull(vm, 0); } else { wrenSetSlotDouble(vm, 0, (double)value); } } // Implements String.toNum(). void stringToNum(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); char* endptr; errno = 0; double value = strtod(str, &endptr); if (errno != 0 || endptr == str || *endptr != '\0') { wrenSetSlotNull(vm, 0); } else { wrenSetSlotDouble(vm, 0, value); } } // Implements String.reverse(). void stringReverse(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); char* result = (char*)malloc(len + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } for (int i = 0; i < len; i++) { result[i] = str[len - 1 - i]; } result[len] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.repeat(_). void stringRepeat(WrenVM* vm) { if (!validateNumber(vm, 1, "Count")) return; int len; const char* str = wrenGetSlotBytes(vm, 0, &len); int count = (int)wrenGetSlotDouble(vm, 1); if (count < 0) { wrenSetSlotString(vm, 0, "Count must be non-negative."); wrenAbortFiber(vm, 0); return; } if (count == 0 || len == 0) { wrenSetSlotString(vm, 0, ""); return; } size_t resultLen = len * count; char* result = (char*)malloc(resultLen + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } for (int i = 0; i < count; i++) { memcpy(result + (i * len), str, len); } result[resultLen] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.padStart(_, _). void stringPadStart(WrenVM* vm) { if (!validateNumber(vm, 1, "Length")) return; if (!validateString(vm, 2, "PadString")) return; int strLen; const char* str = wrenGetSlotBytes(vm, 0, &strLen); int targetLen = (int)wrenGetSlotDouble(vm, 1); int padLen; const char* padStr = wrenGetSlotBytes(vm, 2, &padLen); if (targetLen <= strLen || padLen == 0) { wrenSetSlotString(vm, 0, str); return; } int padNeeded = targetLen - strLen; char* result = (char*)malloc(targetLen + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } int pos = 0; while (pos < padNeeded) { int copyLen = (padNeeded - pos < padLen) ? (padNeeded - pos) : padLen; memcpy(result + pos, padStr, copyLen); pos += copyLen; } memcpy(result + padNeeded, str, strLen); result[targetLen] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.padEnd(_, _). void stringPadEnd(WrenVM* vm) { if (!validateNumber(vm, 1, "Length")) return; if (!validateString(vm, 2, "PadString")) return; int strLen; const char* str = wrenGetSlotBytes(vm, 0, &strLen); int targetLen = (int)wrenGetSlotDouble(vm, 1); int padLen; const char* padStr = wrenGetSlotBytes(vm, 2, &padLen); if (targetLen <= strLen || padLen == 0) { wrenSetSlotString(vm, 0, str); return; } int padNeeded = targetLen - strLen; char* result = (char*)malloc(targetLen + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } memcpy(result, str, strLen); int pos = strLen; while (pos < targetLen) { int copyLen = (targetLen - pos < padLen) ? (targetLen - pos) : padLen; memcpy(result + pos, padStr, copyLen); pos += copyLen; } result[targetLen] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.charAt(_). void stringCharAt(WrenVM* vm) { if (!validateNumber(vm, 1, "Index")) return; int len; const char* str = wrenGetSlotBytes(vm, 0, &len); int index = (int)wrenGetSlotDouble(vm, 1); if (index < 0 || index >= len) { wrenSetSlotString(vm, 0, ""); return; } char result[2] = { str[index], '\0' }; wrenSetSlotString(vm, 0, result); } // Implements String.charCodeAt(_). void stringCharCodeAt(WrenVM* vm) { if (!validateNumber(vm, 1, "Index")) return; int len; const char* str = wrenGetSlotBytes(vm, 0, &len); int index = (int)wrenGetSlotDouble(vm, 1); if (index < 0 || index >= len) { wrenSetSlotNull(vm, 0); return; } wrenSetSlotDouble(vm, 0, (unsigned char)str[index]); } // Implements String.isNumeric(). void stringIsNumeric(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); if (len == 0) { wrenSetSlotBool(vm, 0, false); return; } for (int i = 0; i < len; i++) { if (!isdigit((unsigned char)str[i])) { wrenSetSlotBool(vm, 0, false); return; } } wrenSetSlotBool(vm, 0, true); } // Implements String.isAlpha(). void stringIsAlpha(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); if (len == 0) { wrenSetSlotBool(vm, 0, false); return; } for (int i = 0; i < len; i++) { if (!isalpha((unsigned char)str[i])) { wrenSetSlotBool(vm, 0, false); return; } } wrenSetSlotBool(vm, 0, true); } // Implements String.isAlphaNumeric(). void stringIsAlphaNumeric(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); if (len == 0) { wrenSetSlotBool(vm, 0, false); return; } for (int i = 0; i < len; i++) { if (!isalnum((unsigned char)str[i])) { wrenSetSlotBool(vm, 0, false); return; } } wrenSetSlotBool(vm, 0, true); } // Implements String.isEmpty(). void stringIsEmpty(WrenVM* vm) { int len; wrenGetSlotBytes(vm, 0, &len); wrenSetSlotBool(vm, 0, len == 0); } // Implements String.isWhitespace(). void stringIsWhitespace(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); if (len == 0) { wrenSetSlotBool(vm, 0, true); return; } for (int i = 0; i < len; i++) { if (!isspace((unsigned char)str[i])) { wrenSetSlotBool(vm, 0, false); return; } } wrenSetSlotBool(vm, 0, true); } // Implements String.capitalize(). void stringCapitalize(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); if (len == 0) { wrenSetSlotString(vm, 0, ""); return; } char* result = (char*)malloc(len + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } result[0] = toupper((unsigned char)str[0]); for (int i = 1; i < len; i++) { result[i] = tolower((unsigned char)str[i]); } result[len] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.toCamelCase(). void stringToCamelCase(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); char* result = (char*)malloc(len + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } int j = 0; bool capitalizeNext = false; for (int i = 0; i < len; i++) { if (str[i] == '_' || str[i] == '-' || isspace((unsigned char)str[i])) { capitalizeNext = true; } else { if (capitalizeNext && j > 0) { result[j++] = toupper((unsigned char)str[i]); capitalizeNext = false; } else { result[j++] = (j == 0) ? tolower((unsigned char)str[i]) : str[i]; } } } result[j] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.toSnakeCase(). void stringToSnakeCase(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); // Allocate extra space for underscores char* result = (char*)malloc(len * 2 + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } int j = 0; for (int i = 0; i < len; i++) { if (isupper((unsigned char)str[i]) && i > 0 && !isupper((unsigned char)str[i-1])) { result[j++] = '_'; } if (str[i] == '-' || isspace((unsigned char)str[i])) { result[j++] = '_'; } else { result[j++] = tolower((unsigned char)str[i]); } } result[j] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.toKebabCase(). void stringToKebabCase(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); // Allocate extra space for hyphens char* result = (char*)malloc(len * 2 + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } int j = 0; for (int i = 0; i < len; i++) { if (isupper((unsigned char)str[i]) && i > 0 && !isupper((unsigned char)str[i-1])) { result[j++] = '-'; } if (str[i] == '_' || isspace((unsigned char)str[i])) { result[j++] = '-'; } else { result[j++] = tolower((unsigned char)str[i]); } } result[j] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.toPascalCase(). void stringToPascalCase(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); char* result = (char*)malloc(len + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } int j = 0; bool capitalizeNext = true; for (int i = 0; i < len; i++) { if (str[i] == '_' || str[i] == '-' || isspace((unsigned char)str[i])) { capitalizeNext = true; } else { if (capitalizeNext) { result[j++] = toupper((unsigned char)str[i]); capitalizeNext = false; } else { result[j++] = str[i]; } } } result[j] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.swapCase(). void stringSwapCase(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); char* result = (char*)malloc(len + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } for (int i = 0; i < len; i++) { if (isupper((unsigned char)str[i])) { result[i] = tolower((unsigned char)str[i]); } else if (islower((unsigned char)str[i])) { result[i] = toupper((unsigned char)str[i]); } else { result[i] = str[i]; } } result[len] = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // Implements String.substring(_, _). void stringSubstring(WrenVM* vm) { if (!validateNumber(vm, 1, "Start")) return; if (!validateNumber(vm, 2, "End")) return; int len; const char* str = wrenGetSlotBytes(vm, 0, &len); int start = (int)wrenGetSlotDouble(vm, 1); int end = (int)wrenGetSlotDouble(vm, 2); // Handle negative indices if (start < 0) start = 0; if (end < 0) end = 0; if (start > len) start = len; if (end > len) end = len; // Swap if start > end if (start > end) { int temp = start; start = end; end = temp; } wrenSetSlotBytes(vm, 0, str + start, end - start); } // Implements String.slice(_, _). void stringSlice(WrenVM* vm) { if (!validateNumber(vm, 1, "Start")) return; int len; const char* str = wrenGetSlotBytes(vm, 0, &len); int start = (int)wrenGetSlotDouble(vm, 1); int end = len; // Check if end parameter is provided if (wrenGetSlotCount(vm) > 2 && wrenGetSlotType(vm, 2) == WREN_TYPE_NUM) { end = (int)wrenGetSlotDouble(vm, 2); } // Handle negative indices if (start < 0) start = len + start; if (end < 0) end = len + end; // Clamp values if (start < 0) start = 0; if (end < 0) end = 0; if (start > len) start = len; if (end > len) end = len; if (start >= end) { wrenSetSlotString(vm, 0, ""); return; } wrenSetSlotBytes(vm, 0, str + start, end - start); } // Implements String.toBytes(). void stringToBytes(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); wrenSetSlotNewList(vm, 0); for (int i = 0; i < len; i++) { wrenSetSlotDouble(vm, 1, (unsigned char)str[i]); wrenInsertInList(vm, 0, -1, 1); } } // Implements String.fromCharCode(_). void stringFromCharCode(WrenVM* vm) { if (!validateNumber(vm, 1, "CharCode")) return; int code = (int)wrenGetSlotDouble(vm, 1); if (code < 0 || code > 255) { wrenSetSlotString(vm, 0, ""); return; } char result[2] = { (char)code, '\0' }; wrenSetSlotString(vm, 0, result); } // Implements String.length(). void stringLength(WrenVM* vm) { int len; const char* str = wrenGetSlotBytes(vm, 0, &len); wrenSetSlotDouble(vm, 0, (double)len); } // Static method: String.join(_, _). void stringJoin(WrenVM* vm) { // First argument should be a list if (wrenGetSlotType(vm, 1) != WREN_TYPE_LIST) { wrenSetSlotString(vm, 0, "First argument must be a list."); wrenAbortFiber(vm, 0); return; } if (!validateString(vm, 2, "Separator")) return; int sepLen; const char* separator = wrenGetSlotBytes(vm, 2, &sepLen); int count = wrenGetListCount(vm, 1); if (count == 0) { wrenSetSlotString(vm, 0, ""); return; } // Calculate total length needed size_t totalLen = 0; for (int i = 0; i < count; i++) { wrenGetListElement(vm, 1, i, 0); if (wrenGetSlotType(vm, 0) == WREN_TYPE_STRING) { int itemLen; wrenGetSlotBytes(vm, 0, &itemLen); totalLen += itemLen; } } totalLen += (count - 1) * sepLen; char* result = (char*)malloc(totalLen + 1); if (!result) { wrenSetSlotString(vm, 0, "Memory allocation failed."); wrenAbortFiber(vm, 0); return; } char* p = result; for (int i = 0; i < count; i++) { wrenGetListElement(vm, 1, i, 0); if (wrenGetSlotType(vm, 0) == WREN_TYPE_STRING) { int itemLen; const char* item = wrenGetSlotBytes(vm, 0, &itemLen); memcpy(p, item, itemLen); p += itemLen; if (i < count - 1) { memcpy(p, separator, sepLen); p += sepLen; } } } *p = '\0'; wrenSetSlotString(vm, 0, result); free(result); } // 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) return NULL; // Instance methods if (!isStatic) { 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; if (strcmp(signature, "toUpper()") == 0) return stringToUpper; if (strcmp(signature, "toLower()") == 0) return stringToLower; if (strcmp(signature, "trim()") == 0) return stringTrim; if (strcmp(signature, "trimStart()") == 0) return stringTrimStart; if (strcmp(signature, "trimEnd()") == 0) return stringTrimEnd; if (strcmp(signature, "count(_)") == 0) return stringCount; if (strcmp(signature, "indexOf(_)") == 0) return stringIndexOf; if (strcmp(signature, "lastIndexOf(_)") == 0) return stringLastIndexOf; if (strcmp(signature, "contains(_)") == 0) return stringContains; if (strcmp(signature, "toInt()") == 0) return stringToInt; if (strcmp(signature, "toNum()") == 0) return stringToNum; if (strcmp(signature, "reverse()") == 0) return stringReverse; if (strcmp(signature, "repeat(_)") == 0) return stringRepeat; if (strcmp(signature, "padStart(_,_)") == 0) return stringPadStart; if (strcmp(signature, "padEnd(_,_)") == 0) return stringPadEnd; if (strcmp(signature, "charAt(_)") == 0) return stringCharAt; if (strcmp(signature, "charCodeAt(_)") == 0) return stringCharCodeAt; if (strcmp(signature, "isNumeric()") == 0) return stringIsNumeric; if (strcmp(signature, "isAlpha()") == 0) return stringIsAlpha; if (strcmp(signature, "isAlphaNumeric()") == 0) return stringIsAlphaNumeric; if (strcmp(signature, "isEmpty()") == 0) return stringIsEmpty; if (strcmp(signature, "isWhitespace()") == 0) return stringIsWhitespace; if (strcmp(signature, "capitalize()") == 0) return stringCapitalize; if (strcmp(signature, "toCamelCase()") == 0) return stringToCamelCase; if (strcmp(signature, "toSnakeCase()") == 0) return stringToSnakeCase; if (strcmp(signature, "toKebabCase()") == 0) return stringToKebabCase; if (strcmp(signature, "toPascalCase()") == 0) return stringToPascalCase; if (strcmp(signature, "swapCase()") == 0) return stringSwapCase; if (strcmp(signature, "substring(_,_)") == 0) return stringSubstring; if (strcmp(signature, "slice(_,_)") == 0) return stringSlice; if (strcmp(signature, "slice(_)") == 0) return stringSlice; if (strcmp(signature, "toBytes()") == 0) return stringToBytes; if (strcmp(signature, "length()") == 0) return stringLength; // Added length method } // Static methods if (isStatic) { if (strcmp(signature, "join(_,_)") == 0) return stringJoin; if (strcmp(signature, "fromCharCode(_)") == 0) return stringFromCharCode; } return NULL; }