summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPiotr Fusik <piotr@fusion-lang.org>2024-02-27 21:17:44 +0100
committerPiotr Fusik <piotr@fusion-lang.org>2024-02-27 21:28:50 +0100
commit52501bb91457f93a873b532eff2bbdbb3617902f (patch)
tree9e68b0a758ea446583bcac23c4d2c2648d6cfa82
parent4123ee2e295f8c20695c716d89c3cf6bccbc6165 (diff)
[cpp] Accept non-null-terminated std::string_view in TryParse.
-rw-r--r--GenCpp.fu40
-rw-r--r--libfut.cpp50
-rw-r--r--libfut.cs41
-rw-r--r--libfut.hpp1
-rw-r--r--libfut.js40
-rw-r--r--test/Environment.fu2
6 files changed, 84 insertions, 90 deletions
diff --git a/GenCpp.fu b/GenCpp.fu
index 0c86967..9c27424 100644
--- a/GenCpp.fu
+++ b/GenCpp.fu
@@ -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)");
diff --git a/libfut.cpp b/libfut.cpp
index 4d5c7f1..374168a 100644
--- a/libfut.cpp
+++ b/libfut.cpp
@@ -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)");
diff --git a/libfut.cs b/libfut.cs
index 8b6849c..e02f696 100644
--- a/libfut.cs
+++ b/libfut.cs
@@ -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)");
diff --git a/libfut.hpp b/libfut.hpp
index b266255..c6bfeb3 100644
--- a/libfut.hpp
+++ b/libfut.hpp
@@ -2306,6 +2306,7 @@ public:
private:
bool usingStringViewLiterals;
bool hasEnumFlags;
+ bool numberTryParse;
bool stringReplace;
bool stringToLower;
bool stringToUpper;
diff --git a/libfut.js b/libfut.js
index ceb4585..4a15e1e 100644
--- a/libfut.js
+++ b/libfut.js
@@ -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