diff --git a/repl/lib_compound.c b/repl/lib_compound.c index 0cf62a7..09b6c14 100644 --- a/repl/lib_compound.c +++ b/repl/lib_compound.c @@ -643,6 +643,182 @@ static int nativeTrim(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) return 1; } +static int nativeTrimBegin(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + if (arguments->count < 1 || arguments->count > 2) { + interpreter->errorOutput("Incorrect number of arguments to _trimBegin\n"); + return -1; + } + + //get the arguments + Toy_Literal trimCharsLiteral; + Toy_Literal selfLiteral; + + if (arguments->count == 2) { + trimCharsLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal trimCharsLiteralIdn = trimCharsLiteral; + if (TOY_IS_IDENTIFIER(trimCharsLiteral) && Toy_parseIdentifierToValue(interpreter, &trimCharsLiteral)) { + Toy_freeLiteral(trimCharsLiteralIdn); + } + } + else { + trimCharsLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(" \t\n\r")); + } + selfLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + if (!TOY_IS_STRING(selfLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _trimBegin\n"); + Toy_freeLiteral(trimCharsLiteral); + Toy_freeLiteral(selfLiteral); + return -1; + } + + //unwrap the arguments + Toy_RefString* trimCharsRefString = TOY_AS_STRING(trimCharsLiteral); + Toy_RefString* selfRefString = TOY_AS_STRING(selfLiteral); + + //allocate space for the new string + int bufferBegin = 0; + int bufferEnd = Toy_lengthRefString(selfRefString); + + //for each character in self, check it against each character in trimChars - on a fail, go to end + for (int i = 0; i < Toy_lengthRefString(selfRefString); i++) { + int trimIndex = 0; + + while (trimIndex < Toy_lengthRefString(trimCharsRefString)) { + //there is a match - DON'T increment anymore + if (Toy_toCString(selfRefString)[i] == Toy_toCString(trimCharsRefString)[trimIndex]) { + break; + } + + trimIndex++; + } + + //if the match is found, increment buffer begin + if (trimIndex < Toy_lengthRefString(trimCharsRefString)) { + bufferBegin++; + continue; + } + else { + break; + } + } + + //generate the result + Toy_Literal resultLiteral; + if (bufferBegin >= bufferEnd) { //catch errors + resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("")); + } + else { + char buffer[TOY_MAX_STRING_LENGTH]; + snprintf(buffer, bufferEnd - bufferBegin + 1, "%s", &Toy_toCString(selfRefString)[ bufferBegin ]); + resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(buffer)); //internal copy + } + + //wrap up the buffer and return it + Toy_pushLiteralArray(&interpreter->stack, resultLiteral); //internal copy + + //cleanup + Toy_freeLiteral(resultLiteral); + Toy_freeLiteral(trimCharsLiteral); + Toy_freeLiteral(selfLiteral); + + return 1; +} + +static int nativeTrimEnd(Toy_Interpreter* interpreter, Toy_LiteralArray* arguments) { + if (arguments->count < 1 || arguments->count > 2) { + interpreter->errorOutput("Incorrect number of arguments to _trimEnd\n"); + return -1; + } + + //get the arguments + Toy_Literal trimCharsLiteral; + Toy_Literal selfLiteral; + + if (arguments->count == 2) { + trimCharsLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal trimCharsLiteralIdn = trimCharsLiteral; + if (TOY_IS_IDENTIFIER(trimCharsLiteral) && Toy_parseIdentifierToValue(interpreter, &trimCharsLiteral)) { + Toy_freeLiteral(trimCharsLiteralIdn); + } + } + else { + trimCharsLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(" \t\n\r")); + } + selfLiteral = Toy_popLiteralArray(arguments); + + Toy_Literal selfLiteralIdn = selfLiteral; + if (TOY_IS_IDENTIFIER(selfLiteral) && Toy_parseIdentifierToValue(interpreter, &selfLiteral)) { + Toy_freeLiteral(selfLiteralIdn); + } + + if (!TOY_IS_STRING(selfLiteral)) { + interpreter->errorOutput("Incorrect argument type passed to _trimEnd\n"); + Toy_freeLiteral(trimCharsLiteral); + Toy_freeLiteral(selfLiteral); + return -1; + } + + //unwrap the arguments + Toy_RefString* trimCharsRefString = TOY_AS_STRING(trimCharsLiteral); + Toy_RefString* selfRefString = TOY_AS_STRING(selfLiteral); + + //allocate space for the new string + int bufferBegin = 0; + int bufferEnd = Toy_lengthRefString(selfRefString); + + //again, from the back + for (int i = Toy_lengthRefString(selfRefString); i >= 0; i--) { + int trimIndex = 0; + + while (trimIndex < Toy_lengthRefString(trimCharsRefString)) { + //there is a match - DON'T increment anymore + if (Toy_toCString(selfRefString)[i-1] == Toy_toCString(trimCharsRefString)[trimIndex]) { + break; + } + + trimIndex++; + } + + //if the match is found, increment buffer begin + if (trimIndex < Toy_lengthRefString(trimCharsRefString)) { + bufferEnd--; + continue; + } + else { + break; + } + } + + //generate the result + Toy_Literal resultLiteral; + if (bufferBegin >= bufferEnd) { //catch errors + resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString("")); + } + else { + char buffer[TOY_MAX_STRING_LENGTH]; + snprintf(buffer, bufferEnd - bufferBegin + 1, "%s", &Toy_toCString(selfRefString)[ bufferBegin ]); + resultLiteral = TOY_TO_STRING_LITERAL(Toy_createRefString(buffer)); //internal copy + } + + //wrap up the buffer and return it + Toy_pushLiteralArray(&interpreter->stack, resultLiteral); //internal copy + + //cleanup + Toy_freeLiteral(resultLiteral); + Toy_freeLiteral(trimCharsLiteral); + Toy_freeLiteral(selfLiteral); + + return 1; +} + //call the hook typedef struct Natives { char* name; @@ -672,6 +848,8 @@ int Toy_hookCompound(Toy_Interpreter* interpreter, Toy_Literal identifier, Toy_L {"_toString", nativeToString}, //array, dictionary {"_toUpper", nativeToUpper}, //string {"_trim", nativeTrim}, //string + {"_trimBegin", nativeTrimBegin}, //string + {"_trimEnd", nativeTrimEnd}, //string {NULL, NULL} }; diff --git a/test/scripts/lib/compound.toy b/test/scripts/lib/compound.toy index ecaa041..0182df2 100644 --- a/test/scripts/lib/compound.toy +++ b/test/scripts/lib/compound.toy @@ -137,33 +137,38 @@ import compound; //test trim defaults { - //test a bunch - fn test(s, pass) { - var result = s.trim(); - assert result == pass, "_trim(" + result + ") failed"; + { + //test a bunch + fn test(s, pass) { + var result = s.trim(); + assert result == pass, "_trim(" + result + ") failed"; + } + + test("hello world", "hello world"); + test(" hello world", "hello world"); + test("hello world ", "hello world"); + test(" hello world ", "hello world"); + test(" hello world", "hello world"); + test("hello world ", "hello world"); + test(" hello world ", "hello world"); + test(" hello world", "hello world"); + test("hello world ", "hello world"); + test(" hello world ", "hello world"); + + //one for goot luck + assert " hello world ".trim() == "hello world", "hello world.trim() failed"; } - test("hello world", "hello world"); - test(" hello world", "hello world"); - test("hello world ", "hello world"); - test(" hello world ", "hello world"); - test(" hello world", "hello world"); - test("hello world ", "hello world"); - test(" hello world ", "hello world"); - test(" hello world", "hello world"); - test("hello world ", "hello world"); - test(" hello world ", "hello world"); + //test trim custom values + { + var chars = "heilod"; - //one for goot luck - assert " hello world ".trim() == "hello world", "hello world.trim() failed"; -} + assert "hello world".trim(chars) == " wor", "custom _trim() failed"; + } - -//test trim custom values -{ - var chars = "heilod"; - - assert "hello world".trim(chars) == " wor", "custom _trim() failed"; + //test trimBegin() + assert " foo ".trimBegin() == "foo ", "string.trimBegin() failed"; + assert " foo ".trimEnd() == " foo", "string.trimBegin() failed"; }