diff options
author | Piotr Fusik <piotr@fusion-lang.org> | 2024-02-27 21:17:44 +0100 |
---|---|---|
committer | Piotr Fusik <piotr@fusion-lang.org> | 2024-02-27 21:28:50 +0100 |
commit | 52501bb91457f93a873b532eff2bbdbb3617902f (patch) | |
tree | 9e68b0a758ea446583bcac23c4d2c2648d6cfa82 | |
parent | 4123ee2e295f8c20695c716d89c3cf6bccbc6165 (diff) |
[cpp] Accept non-null-terminated std::string_view in TryParse.
-rw-r--r-- | GenCpp.fu | 40 | ||||
-rw-r--r-- | libfut.cpp | 50 | ||||
-rw-r--r-- | libfut.cs | 41 | ||||
-rw-r--r-- | libfut.hpp | 1 | ||||
-rw-r--r-- | libfut.js | 40 | ||||
-rw-r--r-- | test/Environment.fu | 2 |
6 files changed, 84 insertions, 90 deletions
@@ -22,6 +22,7 @@ public class GenCpp : GenCCpp { bool UsingStringViewLiterals; bool HasEnumFlags; + bool NumberTryParse; bool StringReplace; bool StringToLower; bool StringToUpper; @@ -599,7 +600,7 @@ public class GenCpp : GenCCpp if (expr is FuLiteralString) expr.Accept(this, FuPriority.Argument); else - WritePostfix(expr, ".data()"); + WritePostfix(expr, ".c_str()"); } void WriteRegex!(List<FuExpr#> args, int argIndex) @@ -759,25 +760,11 @@ public class GenCpp : GenCCpp break; case FuId.IntTryParse: case FuId.LongTryParse: - Include("cstdlib"); - Write("[&] { char *ciend; "); - obj.Accept(this, FuPriority.Assign); - Write(" = std::strtol"); - if (method.Id == FuId.LongTryParse) - WriteChar('l'); - WriteChar('('); - WriteCString(args[0]); - Write(", &ciend"); - WriteTryParseRadix(args); - Write("); return *ciend == '\\0'; }()"); // TODO: && *s != '\0' - break; case FuId.DoubleTryParse: - Include("cstdlib"); - Write("[&] { char *ciend; "); - obj.Accept(this, FuPriority.Assign); - Write(" = std::strtod("); - WriteCString(args[0]); - Write(", &ciend); return *ciend == '\\0'; }()"); // TODO: && *s != '\0' + Include("charconv"); + Include("string_view"); + this.NumberTryParse = true; + WriteCall("FuNumber_TryParse", obj, args[0], args.Count == 2 ? args[1] : null); break; case FuId.StringContains: if (parent > FuPriority.Equality) @@ -1570,9 +1557,7 @@ public class GenCpp : GenCCpp if (type.Id == FuId.StringStorageType && expr.Type.Id == FuId.StringPtrType && !(expr is FuLiteral)) { - Write("std::string("); - expr.Accept(this, FuPriority.Argument); - WriteChar(')'); + WriteCall("std::string", expr); } else { FuCallExpr? call = IsStringSubstring(expr); @@ -1930,6 +1915,7 @@ public class GenCpp : GenCCpp this.InHeaderFile = true; this.UsingStringViewLiterals = false; this.HasEnumFlags = false; + this.NumberTryParse = false; this.StringReplace = false; this.StringToLower = false; this.StringToUpper = false; @@ -1981,6 +1967,16 @@ public class GenCpp : GenCCpp CreateImplementationFile(program, ".hpp"); if (this.UsingStringViewLiterals) WriteLine("using namespace std::string_view_literals;"); + if (this.NumberTryParse) { + WriteNewLine(); + WriteLine("template <class T, class... Args>"); + WriteLine("bool FuNumber_TryParse(T &number, std::string_view s, Args... args)"); + OpenBlock(); + WriteLine("const char *end = s.begin() + s.size();"); + WriteLine("auto result = std::from_chars(s.begin(), end, number, args...);"); + WriteLine("return result.ec == std::errc{} && result.ptr == end;"); + CloseBlock(); + } if (this.StringReplace) { WriteNewLine(); WriteLine("static std::string FuString_Replace(std::string_view s, std::string_view oldValue, std::string_view newValue)"); @@ -1,11 +1,20 @@ // Generated automatically with "fut". Do not edit. #include <algorithm> #include <cassert> +#include <charconv> #include <cmath> #include <cstdlib> #include <format> #include "libfut.hpp" +template <class T, class... Args> +bool FuNumber_TryParse(T &number, std::string_view s, Args... args) +{ + const char *end = s.begin() + s.size(); + auto result = std::from_chars(s.begin(), end, number, args...); + return result.ec == std::errc{} && result.ptr == end; +} + static std::string FuString_Replace(std::string_view s, std::string_view oldValue, std::string_view newValue) { std::string result; @@ -2889,7 +2898,7 @@ void FuParser::checkXcrementParent() std::shared_ptr<FuLiteralDouble> FuParser::parseDouble() { double d; - if (![&] { char *ciend; d = std::strtod(FuString_Replace(getLexeme(), "_", "").data(), &ciend); return *ciend == '\0'; }()) + if (!FuNumber_TryParse(d, FuString_Replace(getLexeme(), "_", ""))) reportError("Invalid floating-point number"); std::shared_ptr<FuLiteralDouble> result = std::make_shared<FuLiteralDouble>(); result->line = this->line; @@ -13526,7 +13535,7 @@ void GenCpp::writeCString(const FuExpr * expr) if (dynamic_cast<const FuLiteralString *>(expr)) expr->accept(this, FuPriority::argument); else - writePostfix(expr, ".data()"); + writePostfix(expr, ".c_str()"); } void GenCpp::writeRegex(const std::vector<std::shared_ptr<FuExpr>> * args, int argIndex) @@ -13683,25 +13692,11 @@ void GenCpp::writeCallExpr(const FuExpr * obj, const FuMethod * method, const st break; case FuId::intTryParse: case FuId::longTryParse: - include("cstdlib"); - write("[&] { char *ciend; "); - obj->accept(this, FuPriority::assign); - write(" = std::strtol"); - if (method->id == FuId::longTryParse) - writeChar('l'); - writeChar('('); - writeCString((*args)[0].get()); - write(", &ciend"); - writeTryParseRadix(args); - write("); return *ciend == '\\0'; }()"); - break; case FuId::doubleTryParse: - include("cstdlib"); - write("[&] { char *ciend; "); - obj->accept(this, FuPriority::assign); - write(" = std::strtod("); - writeCString((*args)[0].get()); - write(", &ciend); return *ciend == '\\0'; }()"); + include("charconv"); + include("string_view"); + this->numberTryParse = true; + writeCall("FuNumber_TryParse", obj, (*args)[0].get(), std::ssize(*args) == 2 ? (*args)[1].get() : nullptr); break; case FuId::stringContains: if (parent > FuPriority::equality) @@ -14493,9 +14488,7 @@ void GenCpp::visitLock(const FuLock * statement) void GenCpp::writeStronglyCoerced(const FuType * type, const FuExpr * expr) { if (type->id == FuId::stringStorageType && expr->type->id == FuId::stringPtrType && !dynamic_cast<const FuLiteral *>(expr)) { - write("std::string("); - expr->accept(this, FuPriority::argument); - writeChar(')'); + writeCall("std::string", expr); } else { const FuCallExpr * call = isStringSubstring(expr); @@ -14839,6 +14832,7 @@ void GenCpp::writeProgram(const FuProgram * program) this->inHeaderFile = true; this->usingStringViewLiterals = false; this->hasEnumFlags = false; + this->numberTryParse = false; this->stringReplace = false; this->stringToLower = false; this->stringToUpper = false; @@ -14887,6 +14881,16 @@ void GenCpp::writeProgram(const FuProgram * program) createImplementationFile(program, ".hpp"); if (this->usingStringViewLiterals) writeLine("using namespace std::string_view_literals;"); + if (this->numberTryParse) { + writeNewLine(); + writeLine("template <class T, class... Args>"); + writeLine("bool FuNumber_TryParse(T &number, std::string_view s, Args... args)"); + openBlock(); + writeLine("const char *end = s.begin() + s.size();"); + writeLine("auto result = std::from_chars(s.begin(), end, number, args...);"); + writeLine("return result.ec == std::errc{} && result.ptr == end;"); + closeBlock(); + } if (this->stringReplace) { writeNewLine(); writeLine("static std::string FuString_Replace(std::string_view s, std::string_view oldValue, std::string_view newValue)"); @@ -13341,6 +13341,8 @@ namespace Fusion bool HasEnumFlags; + bool NumberTryParse; + bool StringReplace; bool StringToLower; @@ -13916,7 +13918,7 @@ namespace Fusion if (expr is FuLiteralString) expr.Accept(this, FuPriority.Argument); else - WritePostfix(expr, ".data()"); + WritePostfix(expr, ".c_str()"); } void WriteRegex(List<FuExpr> args, int argIndex) @@ -14072,25 +14074,11 @@ namespace Fusion break; case FuId.IntTryParse: case FuId.LongTryParse: - Include("cstdlib"); - Write("[&] { char *ciend; "); - obj.Accept(this, FuPriority.Assign); - Write(" = std::strtol"); - if (method.Id == FuId.LongTryParse) - WriteChar('l'); - WriteChar('('); - WriteCString(args[0]); - Write(", &ciend"); - WriteTryParseRadix(args); - Write("); return *ciend == '\\0'; }()"); - break; case FuId.DoubleTryParse: - Include("cstdlib"); - Write("[&] { char *ciend; "); - obj.Accept(this, FuPriority.Assign); - Write(" = std::strtod("); - WriteCString(args[0]); - Write(", &ciend); return *ciend == '\\0'; }()"); + Include("charconv"); + Include("string_view"); + this.NumberTryParse = true; + WriteCall("FuNumber_TryParse", obj, args[0], args.Count == 2 ? args[1] : null); break; case FuId.StringContains: if (parent > FuPriority.Equality) @@ -14875,9 +14863,7 @@ namespace Fusion protected override void WriteStronglyCoerced(FuType type, FuExpr expr) { if (type.Id == FuId.StringStorageType && expr.Type.Id == FuId.StringPtrType && !(expr is FuLiteral)) { - Write("std::string("); - expr.Accept(this, FuPriority.Argument); - WriteChar(')'); + WriteCall("std::string", expr); } else { FuCallExpr call = IsStringSubstring(expr); @@ -15228,6 +15214,7 @@ namespace Fusion this.InHeaderFile = true; this.UsingStringViewLiterals = false; this.HasEnumFlags = false; + this.NumberTryParse = false; this.StringReplace = false; this.StringToLower = false; this.StringToUpper = false; @@ -15276,6 +15263,16 @@ namespace Fusion CreateImplementationFile(program, ".hpp"); if (this.UsingStringViewLiterals) WriteLine("using namespace std::string_view_literals;"); + if (this.NumberTryParse) { + WriteNewLine(); + WriteLine("template <class T, class... Args>"); + WriteLine("bool FuNumber_TryParse(T &number, std::string_view s, Args... args)"); + OpenBlock(); + WriteLine("const char *end = s.begin() + s.size();"); + WriteLine("auto result = std::from_chars(s.begin(), end, number, args...);"); + WriteLine("return result.ec == std::errc{} && result.ptr == end;"); + CloseBlock(); + } if (this.StringReplace) { WriteNewLine(); WriteLine("static std::string FuString_Replace(std::string_view s, std::string_view oldValue, std::string_view newValue)"); @@ -2306,6 +2306,7 @@ public: private: bool usingStringViewLiterals; bool hasEnumFlags; + bool numberTryParse; bool stringReplace; bool stringToLower; bool stringToUpper; @@ -13760,6 +13760,7 @@ export class GenCpp extends GenCCpp { #usingStringViewLiterals; #hasEnumFlags; + #numberTryParse; #stringReplace; #stringToLower; #stringToUpper; @@ -14339,7 +14340,7 @@ export class GenCpp extends GenCCpp if (expr instanceof FuLiteralString) expr.accept(this, FuPriority.ARGUMENT); else - this.writePostfix(expr, ".data()"); + this.writePostfix(expr, ".c_str()"); } #writeRegex(args, argIndex) @@ -14497,25 +14498,11 @@ export class GenCpp extends GenCCpp break; case FuId.INT_TRY_PARSE: case FuId.LONG_TRY_PARSE: - this.include("cstdlib"); - this.write("[&] { char *ciend; "); - obj.accept(this, FuPriority.ASSIGN); - this.write(" = std::strtol"); - if (method.id == FuId.LONG_TRY_PARSE) - this.writeChar(108); - this.writeChar(40); - this.#writeCString(args[0]); - this.write(", &ciend"); - this.writeTryParseRadix(args); - this.write("); return *ciend == '\\0'; }()"); - break; case FuId.DOUBLE_TRY_PARSE: - this.include("cstdlib"); - this.write("[&] { char *ciend; "); - obj.accept(this, FuPriority.ASSIGN); - this.write(" = std::strtod("); - this.#writeCString(args[0]); - this.write(", &ciend); return *ciend == '\\0'; }()"); + this.include("charconv"); + this.include("string_view"); + this.#numberTryParse = true; + this.writeCall("FuNumber_TryParse", obj, args[0], args.length == 2 ? args[1] : null); break; case FuId.STRING_CONTAINS: if (parent > FuPriority.EQUALITY) @@ -15304,9 +15291,7 @@ export class GenCpp extends GenCCpp writeStronglyCoerced(type, expr) { if (type.id == FuId.STRING_STORAGE_TYPE && expr.type.id == FuId.STRING_PTR_TYPE && !(expr instanceof FuLiteral)) { - this.write("std::string("); - expr.accept(this, FuPriority.ARGUMENT); - this.writeChar(41); + this.writeCall("std::string", expr); } else { let call = GenCpp.isStringSubstring(expr); @@ -15667,6 +15652,7 @@ export class GenCpp extends GenCCpp this.inHeaderFile = true; this.#usingStringViewLiterals = false; this.#hasEnumFlags = false; + this.#numberTryParse = false; this.#stringReplace = false; this.#stringToLower = false; this.#stringToUpper = false; @@ -15716,6 +15702,16 @@ export class GenCpp extends GenCCpp this.createImplementationFile(program, ".hpp"); if (this.#usingStringViewLiterals) this.writeLine("using namespace std::string_view_literals;"); + if (this.#numberTryParse) { + this.writeNewLine(); + this.writeLine("template <class T, class... Args>"); + this.writeLine("bool FuNumber_TryParse(T &number, std::string_view s, Args... args)"); + this.openBlock(); + this.writeLine("const char *end = s.begin() + s.size();"); + this.writeLine("auto result = std::from_chars(s.begin(), end, number, args...);"); + this.writeLine("return result.ec == std::errc{} && result.ptr == end;"); + this.closeBlock(); + } if (this.#stringReplace) { this.writeNewLine(); this.writeLine("static std::string FuString_Replace(std::string_view s, std::string_view oldValue, std::string_view newValue)"); diff --git a/test/Environment.fu b/test/Environment.fu index 6cf2e3a..f25519e 100644 --- a/test/Environment.fu +++ b/test/Environment.fu @@ -3,7 +3,7 @@ public static class Test public static bool Run() { string s = "HOME"; - string? homeDir = Environment.GetEnvironmentVariable(s); //FAIL: cl + string? homeDir = Environment.GetEnvironmentVariable(s); //FAIL: cpp TODO; cl return homeDir != null && Environment.GetEnvironmentVariable("NOT_EXISTING_ENV_VAR") == null && Environment.GetEnvironmentVariable("") == null |