diff --git a/src/core.c b/src/core.c index ebf951a..8806d32 100644 --- a/src/core.c +++ b/src/core.c @@ -298,7 +298,7 @@ Var varAdd(MSVM* vm, Var v1, Var v2) { case OBJ_STRING: { if (o2->type == OBJ_STRING) { - TODO; // Implement String.format('@@', s1, s2); + return stringFormat(vm, "@@", v1, v2); } } break; diff --git a/src/var.c b/src/var.c index d42aa42..b8dd1da 100644 --- a/src/var.c +++ b/src/var.c @@ -96,16 +96,21 @@ double varToDouble(Var value) { #endif // VAR_NAN_TAGGING } +static String* allocateString(MSVM* vm, size_t length) { + String* string = ALLOCATE_DYNAMIC(vm, String, length + 1, char); + varInitObject(&string->_super, vm, OBJ_STRING); + string->length = length; + string->data[length] = '\0'; + return string; +} + String* newString(MSVM* vm, const char* text, uint32_t length) { ASSERT(length == 0 || text != NULL, "Unexpected NULL string."); - String* string = ALLOCATE_DYNAMIC(vm, String, length + 1, char); - varInitObject(&string->_super, vm, OBJ_STRING); - string->length = length; + String* string = allocateString(vm, length); - if (length != 0) memcpy(string->data, text, length); - string->data[length] = '\0'; + if (length != 0 && text != NULL) memcpy(string->data, text, length); return string; } @@ -363,3 +368,58 @@ bool toBool(Var v) { return true; } + +Var stringFormat(MSVM* vm, const char* fmt, ...) { + va_list arg_list; + + // Calculate the total length of the resulting string. This is required to + // determine the final string size to allocate. + va_start(arg_list, fmt); + size_t total_length = 0; + for (const char* c = fmt; *c != '\0'; c++) { + switch (*c) { + case '$': + total_length += strlen(va_arg(arg_list, const char*)); + break; + + case '@': + total_length += AS_STRING(va_arg(arg_list, Var))->length; + break; + + default: + total_length++; + } + } + va_end(arg_list); + + // Now build the new string. + String* result = allocateString(vm, total_length); + va_start(arg_list, fmt); + char* buff = result->data; + for (const char* c = fmt; *c != '\0'; c++) { + switch (*c) { + case '$': + { + const char* string = va_arg(arg_list, const char*); + size_t length = strlen(string); + memcpy(buff, string, length); + buff += length; + } break; + + case '@': + { + String* string = AS_STRING(va_arg(arg_list, Var)); + memcpy(buff, string->data, string->length); + buff += string->length; + } break; + + default: + { + *buff++ = *c; + } break; + } + } + va_end(arg_list); + + return VAR_OBJ(result); +} \ No newline at end of file diff --git a/src/var.h b/src/var.h index cf45393..60604db 100644 --- a/src/var.h +++ b/src/var.h @@ -378,4 +378,10 @@ String* toString(MSVM* vm, Var v, bool _recursive); // Returns the truthy value of the var. bool toBool(Var v); +// Creates a new string from the arguments. This is intented to used internal +// usage and it has 2 formated characters (just like wren does). +// $ - a C string +// @ - a String object +Var stringFormat(MSVM* vm, const char* fmt, ...); + #endif // VAR_H