From 2a455a048762feb2903ee7f31be45f73ebeb7937 Mon Sep 17 00:00:00 2001 From: PuqiAR Date: Mon, 29 Dec 2025 18:27:39 +0800 Subject: [PATCH] =?UTF-8?q?[Feat]=20=E5=AE=9E=E7=8E=B0=20std.formater?= =?UTF-8?q?=E5=BA=93=EF=BC=8C=E5=A2=9E=E5=8A=A0=20std.io.printf=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20[Fix]=20=E4=BF=AE=E5=A4=8D=20evaluator=E4=B8=AD?= =?UTF-8?q?=E5=A4=84=E7=90=86else=20if=20condVal=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E4=BD=8E=E7=BA=A7=E9=97=AE=E9=A2=98?= =?UTF-8?q?=20[Feat]=20=E4=B8=BA=20String=E7=B1=BB=E5=9E=8B=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20insert,=20replace,=20erase=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/fig_string.hpp | 69 ++++++- src/Evaluator/evaluator.cpp | 15 +- src/Module/Library/std/formater/formater.fig | 178 +++++++++++++++++++ src/Module/Library/std/io/io.fig | 7 + src/Module/builtins.hpp | 2 +- src/Value/value.hpp | 78 +++++++- 6 files changed, 335 insertions(+), 14 deletions(-) create mode 100644 src/Module/Library/std/formater/formater.fig diff --git a/src/Core/fig_string.hpp b/src/Core/fig_string.hpp index 2748812..8c75acd 100644 --- a/src/Core/fig_string.hpp +++ b/src/Core/fig_string.hpp @@ -115,7 +115,7 @@ namespace Fig } i += cplen; - ++ cnt; + ++cnt; } return ch; @@ -138,8 +138,71 @@ namespace Fig if (cnt == index) { - *this = FString(substr(0, i)) + src + - FString(substr(i + cplen)); + *this = FString(substr(0, i)) + src + FString(substr(i + cplen)); + } + + i += cplen; + ++cnt; + } + } + void realErase(size_t index, size_t n) + { + size_t cnt = 0; + size_t eraseStart = 0; + size_t eraseCplens = 0; + for (size_t i = 0; i < size();) + { + uint8_t cplen = 1; + if ((at(i) & 0xf8) == 0xf0) + cplen = 4; + else if ((at(i) & 0xf0) == 0xe0) + cplen = 3; + else if ((at(i) & 0xe0) == 0xc0) + cplen = 2; + if (i + cplen > size()) + cplen = 1; + + i += cplen; + ++cnt; + + if (cnt == index) + { + eraseStart = i; + } + if (cnt < index + n) + { + eraseCplens += cplen; + } + } + erase(eraseStart, eraseCplens); + } + + void realInsert(size_t index, const FString &src) + { + if (index == length()) + { + for (auto &c : src) + { + push_back(c); + } + return; + } + size_t cnt = 0; + for (size_t i = 0; i < size();) + { + uint8_t cplen = 1; + if ((at(i) & 0xf8) == 0xf0) + cplen = 4; + else if ((at(i) & 0xf0) == 0xe0) + cplen = 3; + else if ((at(i) & 0xe0) == 0xc0) + cplen = 2; + if (i + cplen > size()) + cplen = 1; + + if (cnt == index) + { + insert(i, src); } i += cplen; diff --git a/src/Evaluator/evaluator.cpp b/src/Evaluator/evaluator.cpp index 26366e7..717fdd3 100644 --- a/src/Evaluator/evaluator.cpp +++ b/src/Evaluator/evaluator.cpp @@ -1037,12 +1037,15 @@ namespace Fig for (const auto &elif : ifSt->elifs) { ObjectPtr elifCondVal = eval(elif->condition, ctx); - throw EvaluatorError( - u8"TypeError", - std::format( - "Condition must be boolean, but got '{}'", - condVal->getTypeInfo().toString().toBasicString()), - ifSt->condition); + if (elifCondVal->getTypeInfo() != ValueType::Bool) + { + throw EvaluatorError( + u8"TypeError", + std::format( + "Condition must be boolean, but got '{}'", + condVal->getTypeInfo().toString().toBasicString()), + ifSt->condition); + } if (elifCondVal->as()) { return evalBlockStatement(elif->body, ctx); diff --git a/src/Module/Library/std/formater/formater.fig b/src/Module/Library/std/formater/formater.fig new file mode 100644 index 0000000..31bb81c --- /dev/null +++ b/src/Module/Library/std/formater/formater.fig @@ -0,0 +1,178 @@ +/* + Official Module `std.formater` + Library/std/formater/formater.fig +*/ + +import std.value; // `type` function and string_from + +public func format(objects ...) -> Any +{ + if objects.length() < 1 + { + return null; + } + + var fmt := objects[0]; + if value.type(fmt) != "String" + { + return null; + } + + var result := ""; + var argIndex := 1; + var i := 0; + var length := fmt.length(); + + while (i < length) + { + var char := fmt[i]; + + if char == "{" + { + if (i + 1 >= length) + { + return null; + } + + var nextChar = fmt[i + 1]; + + if nextChar == "{" + { + result += "{"; + i += 2; + continue; + } + + var endIndex := -1; + for var j = i + 1; j < length; j += 1 + { + if fmt[j] == "}" + { + endIndex = j; + break; + } + } + + if endIndex == -1 + { + return null; + } + + if argIndex >= objects.length() + { + return null; + } + + result += value.string_from(objects[argIndex]); + argIndex += 1; + + i = endIndex + 1; + } + else if char == "}" + { + if i + 1 < length && fmt[i + 1] == "}" + { + result += "}"; + i += 2; + continue; + } + + return null; + } + else + { + result += char; + i += 1; + } + } + + return result; +} + +public func formatByListArgs(objects) -> Any +{ + if value.type(objects) != "List" + { + return null; + } + if objects.length() < 1 + { + return null; + } + + var fmt := objects[0]; + if value.type(fmt) != "String" + { + return null; + } + + var result := ""; + var argIndex := 1; + var i := 0; + var length := fmt.length(); + + while (i < length) + { + var char := fmt[i]; + + if char == "{" + { + if (i + 1 >= length) + { + return null; + } + + var nextChar = fmt[i + 1]; + + if nextChar == "{" + { + result += "{"; + i += 2; + continue; + } + + var endIndex := -1; + for var j = i + 1; j < length; j += 1 + { + if fmt[j] == "}" + { + endIndex = j; + break; + } + } + + if endIndex == -1 + { + return null; + } + + if argIndex >= objects.length() + { + return null; + } + + result += value.string_from(objects[argIndex]); + argIndex += 1; + + i = endIndex + 1; + } + else if char == "}" + { + if i + 1 < length && fmt[i + 1] == "}" + { + result += "}"; + i += 2; + continue; + } + + return null; + } + else + { + result += char; + i += 1; + } + } + + return result; +} \ No newline at end of file diff --git a/src/Module/Library/std/io/io.fig b/src/Module/Library/std/io/io.fig index c579e96..66c86ef 100644 --- a/src/Module/Library/std/io/io.fig +++ b/src/Module/Library/std/io/io.fig @@ -6,6 +6,8 @@ import _builtins; import noSpace; // sub module `no space print` +import std.formater; // format + // outputs public func print(objects...) -> Int @@ -37,6 +39,11 @@ public func println(objects...) -> Int return length + 1; } +public func printf(objects...) -> Any +{ + __fstdout_print(formater.formatByListArgs(objects)); +} + // inputs public func read() -> String diff --git a/src/Module/builtins.hpp b/src/Module/builtins.hpp index 42af424..36bf62d 100644 --- a/src/Module/builtins.hpp +++ b/src/Module/builtins.hpp @@ -151,7 +151,7 @@ namespace Fig }}, {u8"__fvalue_string_from", [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; - return std::make_shared(val->toString()); + return std::make_shared(val->toStringIO()); }}, /* math start */ {u8"__fmath_acos", [](const std::vector &args) -> ObjectPtr { diff --git a/src/Value/value.hpp b/src/Value/value.hpp index 16b5992..d3fb0e9 100644 --- a/src/Value/value.hpp +++ b/src/Value/value.hpp @@ -78,7 +78,76 @@ namespace Fig const FString &str = as(); return std::make_shared(static_cast(str.length())); }}, - + {u8"replace", [this](std::vector args) -> ObjectPtr { + if (args.size() != 2) + throw RuntimeError(FString( + std::format("`replace` expects 2 arguments, {} got", args.size()))); + FString &str = as(); + ObjectPtr arg1 = args[0]; + ObjectPtr arg2 = args[1]; + if (!arg1->is()) + { + throw RuntimeError(FString( + "`replace` arg 1 expects type Int")); + } + if (!arg2->is()) + { + throw RuntimeError(FString( + "`replace` arg 2 expects type String")); + } + str.realReplace(arg1->as(), arg2->as()); + return Object::getNullInstance(); + }}, + {u8"erase", [this](std::vector args) -> ObjectPtr { + if (args.size() != 2) + throw RuntimeError(FString( + std::format("`erase` expects 2 arguments, {} got", args.size()))); + FString &str = as(); + ObjectPtr arg1 = args[0]; + ObjectPtr arg2 = args[1]; + if (!arg1->is()) + { + throw RuntimeError(FString( + "`erase` arg 1 expects type Int")); + } + if (!arg2->is()) + { + throw RuntimeError(FString( + "`erase` arg 2 expects type Int")); + } + ValueType::IntClass index = arg1->as(); + ValueType::IntClass n = arg2->as(); + if (index < 0 || n < 0) + { + throw RuntimeError(FString("`erase`: index and n must greater or equal to 0")); + } + if (index + n > str.length()) + { + throw RuntimeError(FString("`erase`: length is not long enough to erase")); + } + str.realErase(arg1->as(), arg2->as()); + return Object::getNullInstance(); + }}, + {u8"insert", [this](std::vector args) -> ObjectPtr { + if (args.size() != 2) + throw RuntimeError(FString( + std::format("`insert` expects 2 arguments, {} got", args.size()))); + FString &str = as(); + ObjectPtr arg1 = args[0]; + ObjectPtr arg2 = args[1]; + if (!arg1->is()) + { + throw RuntimeError(FString( + "`insert` arg 1 expects type Int")); + } + if (!arg2->is()) + { + throw RuntimeError(FString( + "`insert` arg 2 expects type String")); + } + str.realInsert(arg1->as(), arg2->as()); + return Object::getNullInstance(); + }}, }}, {ValueType::Function, {}}, {ValueType::StructType, {}}, @@ -136,15 +205,16 @@ namespace Fig map.contains(index)); }}, }}, - {ValueType::Module, {}} - }; + {ValueType::Module, {}}}; std::unordered_map, TypeInfoHash> memberTypeFunctionsParas{ {ValueType::Null, {}}, {ValueType::Int, {}}, {ValueType::Double, {}}, {ValueType::String, { {u8"length", 0}, - + {u8"replace", 2}, + {u8"erase", 2}, + {u8"insert", 2}, }}, {ValueType::Function, {}}, {ValueType::StructType, {}},