FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Add string interpolation · feiyunwill/cppfront@2455a91 · GitHub

Commit 2455a91

Browse files
committed
Add string interpolation
And make postconditions test only on scuccess exits
1 parent 75c1c08 commit 2455a91

7 files changed

Lines changed: 201 additions & 47 deletions

‎include/cpp2util.h‎

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,54 @@ constexpr auto as( std::optional<T> const& x ) -> auto&&
459459
{ return x.value(); }
460460

461461

462+
//-----------------------------------------------------------------------
463+
//
464+
// A variation of GSL's final_action_success and finally to run only on success
465+
// (based on a PR I contributed to Microsoft GSL)
466+
//
467+
// final_action_success_success ensures something is run at the end of a scope
468+
// if no exception is thrown
469+
//
470+
// finally_success is a convenience function to make a final_action_success_success
471+
//
472+
//-----------------------------------------------------------------------
473+
//
474+
475+
template <class F>
476+
class final_action_success
477+
{
478+
public:
479+
explicit final_action_success(const F& ff) noexcept : f{ff} { }
480+
explicit final_action_success(F&& ff) noexcept : f{std::move(ff)} { }
481+
482+
~final_action_success() noexcept
483+
{
484+
if (invoke && ecount == std::uncaught_exceptions()) {
485+
f();
486+
}
487+
}
488+
489+
final_action_success(final_action_success&& other) noexcept
490+
: f(std::move(other.f)), invoke(std::exchange(other.invoke, false))
491+
{ }
492+
493+
final_action_success(const final_action_success&) = delete;
494+
void operator=(const final_action_success&) = delete;
495+
void operator=(final_action_success&&) = delete;
496+
497+
private:
498+
F f;
499+
int ecount = std::uncaught_exceptions();
500+
bool invoke = true;
501+
};
502+
503+
template <class F>
504+
[[nodiscard]] auto finally_success(F&& f) noexcept
505+
{
506+
return final_action_success<std::remove_cvref_t<F>>{std::forward<F>(f)};
507+
}
508+
509+
462510
//-----------------------------------------------------------------------
463511
//
464512
// to_string for string interpolation
@@ -482,7 +530,7 @@ auto to_string(std::optional<T> const& o) -> std::string {
482530
if (o.has_value()) {
483531
return std::to_string(o.value());
484532
}
485-
return "nullopt";
533+
return "(empty)";
486534
}
487535

488536

@@ -513,46 +561,6 @@ constexpr auto narrow_cast(From&& from) noexcept -> To
513561
return static_cast<To>(std::forward<From>(from));
514562
}
515563

516-
517-
//-----------------------------------------------------------------------
518-
//
519-
// An implementation of GSL's final_action and finally
520-
// (based on a PR I contributed to Microsoft GSL)
521-
//
522-
// final_action ensures something is run at the end of a scope
523-
//
524-
// finally is a convenience function to generate a final_action
525-
//
526-
//-----------------------------------------------------------------------
527-
//
528-
template <class F>
529-
class final_action
530-
{
531-
public:
532-
explicit final_action(const F& ff) noexcept : f{ff} { }
533-
explicit final_action(F&& ff) noexcept : f{std::move(ff)} { }
534-
535-
~final_action() noexcept { if (invoke) f(); }
536-
537-
final_action(final_action&& other) noexcept
538-
: f(std::move(other.f)), invoke(std::exchange(other.invoke, false))
539-
{ }
540-
541-
final_action(const final_action&) = delete;
542-
void operator=(const final_action&) = delete;
543-
void operator=(final_action&&) = delete;
544-
545-
private:
546-
F f;
547-
bool invoke = true;
548-
};
549-
550-
template <class F>
551-
[[nodiscard]] auto finally(F&& f) noexcept
552-
{
553-
return final_action<std::remove_cvref_t<F>>{std::forward<F>(f)};
554-
}
555-
556564
}
557565

558566
#endif

‎regression-tests/test-results/mixed-captures-in-expressions-and-postconditions.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ std::vector<int> vec { };
2929
auto insert_at(cpp2::in<int> where, cpp2::in<int> val) -> void
3030
{
3131
cpp2::Default.expects(0 <= where && where <= CPP2_UFCS_0(ssize, vec), "");
32-
auto final18_5 = gsl::finally([_0 = CPP2_UFCS_0(ssize, vec)]{cpp2::Default.expects(CPP2_UFCS_0(ssize, vec) == _0 + 1, "");} );
32+
auto post_18_5 = cpp2::finally_success([_0 = CPP2_UFCS_0(ssize, vec)]{cpp2::Default.expects(CPP2_UFCS_0(ssize, vec) == _0 + 1, "");} );
3333
#line 18 "mixed-captures-in-expressions-and-postconditions.cpp2"
3434

3535

‎regression-tests/test-results/mixed-postexpression-with-capture.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ std::vector<int> vec { };
2727
auto insert_at(cpp2::in<int> where, cpp2::in<int> val) -> void
2828
{
2929
cpp2::Default.expects(0 <= where && where <= CPP2_UFCS_0(ssize, vec), "");
30-
auto final16_5 = gsl::finally([_0 = CPP2_UFCS_0(size, vec)]{cpp2::Default.expects(CPP2_UFCS_0(size, vec) == _0 + 1, "");} );
30+
auto post_16_5 = cpp2::finally_success([_0 = CPP2_UFCS_0(size, vec)]{cpp2::Default.expects(CPP2_UFCS_0(size, vec) == _0 + 1, "");} );
3131
#line 16 "mixed-postexpression-with-capture.cpp2"
3232

3333

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// ----- Cpp2 support -----
2+
#include "cpp2util.h"
3+
4+
#line 1 "mixed-string-interpolation.cpp2"
5+
#include <iostream>
6+
7+
[[nodiscard]] auto main() -> int;
8+
9+
//=== Cpp2 definitions ==========================================================
10+
11+
#line 2 "mixed-string-interpolation.cpp2"
12+
13+
[[nodiscard]] auto main() -> int{
14+
auto a { 2 };
15+
std::optional<int> b { };
16+
std::cout << "a = " + cpp2::to_string(a) + ", b = " + cpp2::to_string(b) + "\n";
17+
18+
b = 42;
19+
std::cout << "a^2 + b = " + cpp2::to_string(a * a + b.value()) + "\n";
20+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <iostream>
2+
3+
main: () -> int = {
4+
a := 2;
5+
b: std::optional<int> = ();
6+
std::cout << "a = (a)$, b = (b)$\n";
7+
8+
b = 42;
9+
std::cout << "a^2 + b = (a * a + b.value())$\n";
10+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mixed-string-interpolation.cpp2... ok (mixed Cpp1/Cpp2, Cpp2 code passes safety checks)
2+

‎source/cppfront.cpp‎

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -846,13 +846,127 @@ class cppfront
846846
}
847847

848848

849+
//-----------------------------------------------------------------------
850+
//
851+
// A StringLiteral could include captures
852+
//
853+
auto expand_string_literal(token const& n) -> std::string
854+
{
855+
assert(n.type() == lexeme::StringLiteral);
856+
857+
auto text = std::string_view(n);
858+
auto length = n.length();
859+
assert (std::ssize(text) == length);
860+
861+
assert(length >= 2);
862+
assert(text.back() == '"');
863+
864+
auto pos = 0;
865+
auto ret = std::string{}; // the return string we're going to build
866+
auto current_start = 0; // the current offset before which the string has been into ret
867+
868+
// Skip prefix to first non-" character
869+
while (pos < length && text[pos] != '"') {
870+
++pos;
871+
}
872+
assert(pos < length && text[pos] == '"');
873+
++pos;
874+
875+
// Now we're on the first character of the string itself
876+
for ( ; pos < length && text[pos] != '"'; ++pos)
877+
{
878+
// Find the next )$
879+
if (text[pos] == '$' && text[pos-1] == ')')
880+
{
881+
// Scan back to find the matching (
882+
auto paren_depth = 1;
883+
auto open = pos - 2;
884+
for( ; text[open] != '"'; --open)
885+
{
886+
if (text[open] == ')') {
887+
++paren_depth;
888+
}
889+
else if (text[open] == '(') {
890+
--paren_depth;
891+
if (paren_depth == 0) {
892+
break;
893+
}
894+
}
895+
else if (
896+
(text[open] == '-' && text[open - 1] == '-') ||
897+
(text[open] == '-' && text[open - 1] == '-')
898+
)
899+
{
900+
errors.emplace_back(
901+
source_position( n.position().lineno, n.position().colno + pos ),
902+
"a string interpolation expression may not contain ++ or --"
903+
);
904+
return "";
905+
}
906+
else if (
907+
(text[open] == '*' && !isspace(text[open - 1])) ||
908+
(text[open] == '&' && !isspace(text[open - 1])) ||
909+
(text[open] == '!' && !isspace(text[open - 1]))
910+
)
911+
{
912+
errors.emplace_back(
913+
source_position( n.position().lineno, n.position().colno + pos ),
914+
"a string interpolation expression may not contain unary *, &, or ~ (if you meant binary, add a space before the operator)"
915+
);
916+
return "";
917+
}
918+
}
919+
if (text[open] == '"')
920+
{
921+
errors.emplace_back(
922+
source_position( n.position().lineno, n.position().colno + pos ),
923+
"no matching ( for string interpolation ending in )$"
924+
);
925+
return "";
926+
}
927+
assert (text[open] == '(');
928+
929+
// 'open' is now at the matching (
930+
931+
// Put the next non-interpolated chunk straight into ret
932+
if (current_start > 0) {
933+
ret += '"';
934+
}
935+
ret += text.substr(current_start, open - current_start);
936+
ret += '"';
937+
938+
// Then put interpolated chunk into ret
939+
ret += " + cpp2::to_string";
940+
ret += text.substr(open, pos - open);
941+
ret += " + ";
942+
943+
current_start = pos+1;
944+
}
945+
}
946+
947+
// Now we should be on the the final " closing the string
948+
assert(pos == length-1 && text[pos] == '"');
949+
950+
// Put the final non-interpolated chunk straight into ret
951+
if (current_start > 0) {
952+
ret += '"';
953+
}
954+
ret += text.substr(current_start);
955+
956+
return ret;
957+
}
958+
959+
849960
//-----------------------------------------------------------------------
850961
//
851962
auto emit(token const& n) -> void
852963
{
853964
if (n == "new") {
854965
printer.print_cpp2("cpp2_new", n.position());
855966
}
967+
else if (n.type() == lexeme::StringLiteral) {
968+
printer.print_cpp2( expand_string_literal(n), n.position() );
969+
}
856970
else {
857971
printer.print_cpp2(n, n.position());
858972
}
@@ -1880,13 +1994,13 @@ class cppfront
18801994
{
18811995
assert (n.kind);
18821996

1883-
// For a postcondition, we'll wrap it in a final_action lambda
1997+
// For a postcondition, we'll wrap it in a final_action_success lambda
18841998
//
18851999
if (*n.kind == "post") {
18862000
auto lambda_intro = build_capture_lambda_intro_for(n.captures, n.position());
18872001
printer.print_cpp2(
1888-
"auto final" + std::to_string(n.position().lineno) + "_" +
1889-
std::to_string(n.position().colno) + " = gsl::finally(" +
2002+
"auto post_" + std::to_string(n.position().lineno) + "_" +
2003+
std::to_string(n.position().colno) + " = cpp2::finally_success(" +
18902004
lambda_intro + "{",
18912005
n.position()
18922006
);
@@ -1934,7 +2048,7 @@ class cppfront
19342048
}
19352049
printer.print_cpp2(");", n.position());
19362050

1937-
// For a postcondition, close out the final_action lambda
2051+
// For a postcondition, close out the final_action_success lambda
19382052
//
19392053
if (*n.kind == "post") {
19402054
printer.print_cpp2( "} );", n.position()

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL