Skip to content

Commit

Permalink
[cpp] Accept non-null-terminated std::string_view in TryParse.
Browse files Browse the repository at this point in the history
  • Loading branch information
pfusik committed Feb 27, 2024
1 parent 4123ee2 commit 52501bb
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 90 deletions.
40 changes: 18 additions & 22 deletions GenCpp.fu
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class GenCpp : GenCCpp
{
bool UsingStringViewLiterals;
bool HasEnumFlags;
bool NumberTryParse;
bool StringReplace;
bool StringToLower;
bool StringToUpper;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)");
Expand Down
50 changes: 27 additions & 23 deletions libfut.cpp
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)");
Expand Down
41 changes: 19 additions & 22 deletions libfut.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13341,6 +13341,8 @@ public class GenCpp : GenCCpp

bool HasEnumFlags;

bool NumberTryParse;

bool StringReplace;

bool StringToLower;
Expand Down Expand Up @@ -13916,7 +13918,7 @@ void WriteCString(FuExpr expr)
if (expr is FuLiteralString)
expr.Accept(this, FuPriority.Argument);
else
WritePostfix(expr, ".data()");
WritePostfix(expr, ".c_str()");
}

void WriteRegex(List<FuExpr> args, int argIndex)
Expand Down Expand Up @@ -14072,25 +14074,11 @@ protected override void WriteCallExpr(FuExpr obj, FuMethod method, List<FuExpr>
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)
Expand Down Expand Up @@ -14875,9 +14863,7 @@ internal override void VisitLock(FuLock statement)
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);
Expand Down Expand Up @@ -15228,6 +15214,7 @@ public override void WriteProgram(FuProgram program)
this.InHeaderFile = true;
this.UsingStringViewLiterals = false;
this.HasEnumFlags = false;
this.NumberTryParse = false;
this.StringReplace = false;
this.StringToLower = false;
this.StringToUpper = false;
Expand Down Expand Up @@ -15276,6 +15263,16 @@ public override void WriteProgram(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)");
Expand Down
1 change: 1 addition & 0 deletions libfut.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2306,6 +2306,7 @@ class GenCpp : public GenCCpp
private:
bool usingStringViewLiterals;
bool hasEnumFlags;
bool numberTryParse;
bool stringReplace;
bool stringToLower;
bool stringToUpper;
Expand Down
40 changes: 18 additions & 22 deletions libfut.js
Original file line number Diff line number Diff line change
Expand Up @@ -13760,6 +13760,7 @@ export class GenCpp extends GenCCpp
{
#usingStringViewLiterals;
#hasEnumFlags;
#numberTryParse;
#stringReplace;
#stringToLower;
#stringToUpper;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)");
Expand Down
2 changes: 1 addition & 1 deletion test/Environment.fu
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 52501bb

Please sign in to comment.