// string_backend.c
#include "wren.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
// 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;
}