#include #include #include #include #include #include #include #include #include GroundValue stringUpper(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; char* uppercase = malloc(sizeof(char) * (strlen(string) + 1)); if (!uppercase) ERROR("Failed to allocate memory while getting uppercase of string!", "AllocFail"); for (size_t i = 0; i < strlen(string); i++) { uppercase[i] = toupper(string[i]); } return groundCreateValue(STRING, uppercase); } GroundValue stringLower(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; char* lowercase = malloc(sizeof(char) * (strlen(string) + 1)); if (!lowercase) ERROR("Failed to allocate memory while getting uppercase of string!", "AllocFail"); for (size_t i = 0; i < strlen(string); i++) { lowercase[i] = tolower(string[i]); } return groundCreateValue(STRING, lowercase); } GroundValue stringStartsWith(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; char* prefix = args.values[1].data.stringVal; return groundCreateValue(BOOL, strncmp(prefix, string, strlen(prefix)) == 0); } GroundValue stringEndsWith(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; char* suffix = args.values[1].data.stringVal; size_t strLen = strlen(string); size_t suffixLen = strlen(suffix); if (suffixLen > strLen) return groundCreateValue(BOOL, false); // can't start with suffix if suffix is longer return groundCreateValue( BOOL, strcmp(string + strLen - suffixLen, suffix) == 0 ); } GroundValue stringSubstring(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; int64_t start = args.values[1].data.intVal; int64_t end = args.values[2].data.intVal; if (end < start) { ERROR("End can't be less than start when getting substring!", "EndBeforeStart"); } int64_t inputLen = strlen(string); if (start >= inputLen) ERROR("Start is outside string!", "OutOfBounds"); else if (end >= inputLen) ERROR("End is outside string!", "OutOfBounds"); char* buffer = malloc(inputLen + 1); if (!buffer) ERROR("Failed to allocate memory while getting substring of string!", "AllocFail"); // string_Substring("Hello!", 1, 3) -> "ell" // amount = 3 - 1 + 1 = 3 strncpy(buffer, string + start, end - start + 1); buffer[end - start + 1] = 0; return groundCreateValue(STRING, buffer); } GroundValue stringCharAt(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; int64_t index = args.values[1].data.intVal; if (index < 0) ERROR("Index can't be less than 0!", "OutOfBounds"); int64_t stringLen = strlen(string); if (index >= stringLen) { char buffer[256]; sprintf(buffer, "Attempt to get char at index %ld when string is of length %ld.", index, stringLen); ERROR(buffer, "OutOfBounds"); } char buffer[2] = {string[index], 0}; return groundCreateValue(STRING, buffer); } GroundValue stringFind(GroundScope* scope, List args) { char* haystack = args.values[0].data.stringVal; char* needle = args.values[1].data.stringVal; char* result = strstr(haystack, needle); if (!result) return groundCreateValue(INT, (int64_t)-1); return groundCreateValue(INT, result - haystack); } GroundValue stringFindLast(GroundScope* scope, List args) { char* haystack = args.values[0].data.stringVal; char* needle = args.values[1].data.stringVal; if (!*needle) { return groundCreateValue(INT, (int64_t)strlen(haystack)-1); } const char* p = haystack; while (*p) p++; const char* q = needle; while (*q) q++; bool found = false; while (!found && (p - haystack) >= (q - needle)) { const char* s = p; const char* t = q; while (t != needle && s != haystack && *(s - 1) == *(t - 1)) { s--; t--; } found = (t == needle); if (found) p = s; else p--; } return groundCreateValue(INT, (int64_t)(found ? p - haystack : -1)); } GroundValue stringContains(GroundScope* scope, List args) { char* haystack = args.values[0].data.stringVal; char* needle = args.values[1].data.stringVal; char* result = strstr(haystack, needle); return groundCreateValue(BOOL, result != NULL); } GroundValue stringCount(GroundScope* scope, List args) { char* haystack = args.values[0].data.stringVal; char* needle = args.values[1].data.stringVal; int64_t count = 0; const char* tmp = haystack; while ((tmp = strstr(tmp, needle))) { count++; tmp++; } return groundCreateValue(INT, count); } GroundValue stringTrim(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; // trip whitespace from start char* start = string; while (*start && isspace((unsigned char)*start)) start++; // trim whitespace from end char* end = string + strlen(string); while (end > start && isspace((unsigned char)*(end - 1))) end--; size_t len = end - start; char* buffer = malloc(len + 1); if (!buffer) ERROR("Failed to allocate memory while trimming string!", "AllocFail"); memcpy(buffer, start, len); buffer[len] = 0; return groundCreateValue(STRING, buffer); } GroundValue stringTrimLeft(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; // trip whitespace from start char* start = string; while (*start && isspace((unsigned char)*start)) start++; size_t len = strlen(start); char* buffer = malloc(len + 1); if (!buffer) ERROR("Failed to allocate memory while trimming string!", "AllocFail"); memcpy(buffer, start, len); buffer[len] = 0; return groundCreateValue(STRING, buffer); } GroundValue stringTrimRight(GroundScope* scope, List args) { char* str = args.values[0].data.stringVal; char* end = str + strlen(str); while (end > str && isspace((unsigned char)*(end - 1))) { end--; } size_t len = end - str; char* buffer = malloc(len + 1); if (!buffer) ERROR("Failed to allocate memory while trimming string!", "AllocFail"); memcpy(buffer, str, len); buffer[len] = '\0'; return groundCreateValue(STRING, buffer); } GroundValue stringReplace(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; char* from = args.values[1].data.stringVal; char* to = args.values[2].data.stringVal; // === THANK YOU TO https://creativeandcritical.net FOR THEIR repl_str() FUNCTION === \\ /* Adjust each of the below values to suit your needs. */ /* Increment positions cache size initially by this number. */ size_t cache_sz_inc = 16; /* Thereafter, each time capacity needs to be increased, * multiply the increment by this factor. */ const size_t cache_sz_inc_factor = 3; /* But never increment capacity by more than this number. */ const size_t cache_sz_inc_max = 1048576; char *pret, *ret = NULL; const char *pstr2, *pstr = string; size_t i, count = 0; #if (__STDC_VERSION__ >= 199901L) uintptr_t *pos_cache_tmp, *pos_cache = NULL; #else ptrdiff_t *pos_cache_tmp, *pos_cache = NULL; #endif size_t cache_sz = 0; size_t cpylen, orglen, retlen, tolen, fromlen = strlen(from); /* Find all matches and cache their positions. */ while ((pstr2 = strstr(pstr, from)) != NULL) { count++; /* Increase the cache size when necessary. */ if (cache_sz < count) { cache_sz += cache_sz_inc; pos_cache_tmp = realloc(pos_cache, sizeof(*pos_cache) * cache_sz); if (pos_cache_tmp == NULL) { goto end_repl_str; } else pos_cache = pos_cache_tmp; cache_sz_inc *= cache_sz_inc_factor; if (cache_sz_inc > cache_sz_inc_max) { cache_sz_inc = cache_sz_inc_max; } } pos_cache[count-1] = pstr2 - string; pstr = pstr2 + fromlen; } orglen = pstr - string + strlen(pstr); /* Allocate memory for the post-replacement string. */ if (count > 0) { tolen = strlen(to); retlen = orglen + (tolen - fromlen) * count; } else retlen = orglen; ret = malloc(retlen + 1); if (ret == NULL) { goto end_repl_str; } if (count == 0) { /* If no matches, then just duplicate the string. */ strcpy(ret, string); } else { /* Otherwise, duplicate the string whilst performing * the replacements using the position cache. */ pret = ret; memcpy(pret, string, pos_cache[0]); pret += pos_cache[0]; for (i = 0; i < count; i++) { memcpy(pret, to, tolen); pret += tolen; pstr = string + pos_cache[i] + fromlen; cpylen = (i == count-1 ? orglen : pos_cache[i+1]) - pos_cache[i] - fromlen; memcpy(pret, pstr, cpylen); pret += cpylen; } ret[retlen] = '\0'; } end_repl_str: /* Free the cache and return the post-replacement string, * which will be NULL in the event of an error. */ free(pos_cache); return groundCreateValue(STRING, ret); } GroundValue stringRepeat(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; int64_t times = args.values[1].data.intVal; if (times < 0) ERROR("Can't repeat string negative number of times!", "ValueError"); char* out = malloc(strlen(string) * times + 1); if (!out) ERROR("Failed to allocate memory while repeating string!", "AllocFail"); strcpy(out, string); while (--times > 0) { strcat(out, string); } return groundCreateValue(STRING, out); } GroundValue stringReverse(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; size_t stringLen = strlen(string); char* out = malloc(stringLen + 1); if (!out) ERROR("Failed to allocate memory while reversing string!", "AllocFail"); unsigned int i = 0; while (stringLen > 0) out[i++] = string[stringLen-- - 1]; out[i] = 0; return groundCreateValue(STRING, out); } GroundValue stringIsAlpha(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; const char* p = string; bool isAlpha = true; while (*p) { if (!isalpha(*p)) { isAlpha = false; break; } p++; } return groundCreateValue(BOOL, isAlpha); } GroundValue stringIsDigit(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; const char* p = string; bool isDigit = true; while (*p) { if (!isdigit(*p)) { isDigit = false; break; } p++; } return groundCreateValue(BOOL, isDigit); } GroundValue stringIsAlnum(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; const char* p = string; bool isAlnum = true; while (*p) { if (!isalnum(*p)) { isAlnum = false; break; } p++; } return groundCreateValue(BOOL, isAlnum); } GroundValue stringIsSpace(GroundScope* scope, List args) { char* string = args.values[0].data.stringVal; const char* p = string; bool isSpace = true; while (*p) { if (!isspace(*p)) { isSpace = false; break; } p++; } return groundCreateValue(BOOL, isSpace); } void ground_init(GroundScope* scope) { // modify string groundAddNativeFunction(scope, "string_Upper", stringUpper, STRING, 1, STRING, "str"); groundAddNativeFunction(scope, "string_Lower", stringLower, STRING, 1, STRING, "str"); groundAddNativeFunction(scope, "string_Trim", stringTrim, STRING, 1, STRING, "str"); groundAddNativeFunction(scope, "string_TrimLeft", stringTrimLeft, STRING, 1, STRING, "str"); groundAddNativeFunction(scope, "string_TrimRight", stringTrimRight, STRING, 1, STRING, "str"); groundAddNativeFunction(scope, "string_Substring", stringSubstring, STRING, 3, STRING, "str", INT, "start", INT, "end"); groundAddNativeFunction(scope, "string_Replace", stringReplace, STRING, 3, STRING, "str", STRING, "from", STRING, "to"); groundAddNativeFunction(scope, "string_Repeat", stringRepeat, STRING, 2, STRING, "str", INT, "times"); groundAddNativeFunction(scope, "string_Reverse", stringReverse, STRING, 1, STRING, "str"); // check for substring groundAddNativeFunction(scope, "string_StartsWith", stringStartsWith, BOOL, 2, STRING, "str", STRING, "prefix"); groundAddNativeFunction(scope, "string_EndsWith", stringEndsWith, BOOL, 2, STRING, "str", STRING, "suffix"); groundAddNativeFunction(scope, "string_Contains", stringContains, BOOL, 2, STRING, "haystack", STRING, "needle"); // character classification groundAddNativeFunction(scope, "string_IsAlpha", stringIsAlpha, BOOL, 1, STRING, "str"); groundAddNativeFunction(scope, "string_IsDigit", stringIsDigit, BOOL, 1, STRING, "str"); groundAddNativeFunction(scope, "string_IsAlnum", stringIsAlnum, BOOL, 1, STRING, "str"); groundAddNativeFunction(scope, "string_IsSpace", stringIsSpace, BOOL, 1, STRING, "str"); // get char groundAddNativeFunction(scope, "string_CharAt", stringCharAt, STRING, 2, STRING, "str", INT, "index"); // find index substring groundAddNativeFunction(scope, "string_Find", stringFind, INT, 2, STRING, "haystack", STRING, "needle"); groundAddNativeFunction(scope, "string_FindLast", stringFindLast, INT, 2, STRING, "haystack", STRING, "needle"); groundAddNativeFunction(scope, "string_Count", stringCount, INT, 2, STRING, "haystack", STRING, "needle"); }