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