1818#include " sema.h"
1919#include < iostream>
2020#include < cstdio>
21-
21+ # include < optional >
2222
2323namespace cpp2 {
2424
@@ -1725,77 +1725,6 @@ class cppfront
17251725 captured_part += " _" + std::to_string (mynum);
17261726 }
17271727
1728- // Check to see if it's just a function call with "." syntax,
1729- // and if so use this path to convert it to UFCS
1730- if (// there's a single-token expression followed by . and (
1731- n.expr ->get_token () && // if the base expression is a single token
1732- std::ssize (n.ops ) >= 2 && // and we're of the form:
1733- n.ops [0 ].op ->type () == lexeme::Dot && // token . id-expr ( expr-list )
1734- n.ops [1 ].op ->type () == lexeme::LeftParen &&
1735- // alpha limitation: if it's a function call with more than one template argument (e.g., x.f<1,2>())
1736- // the UFCS* macros can't handle that right now, so don't UFCS-size it
1737- n.ops [0 ].id_expr ->template_args_count () < 2 &&
1738- // and either there's nothing after that, or there's just a $ after that
1739- (
1740- std::ssize (n.ops ) == 2 ||
1741- (std::ssize (n.ops ) == 3 && n.ops [2 ].op ->type () == lexeme::Dollar)
1742- )
1743- )
1744- {
1745- // If we already replaced this with a capture (which contains the UFCS
1746- // work already done when the capture was computed), emit the capture
1747- if (!captured_part.empty ()) {
1748- printer.print_cpp2 (captured_part, n.position ());
1749- return ;
1750- }
1751-
1752- // Otherwise, do the UFCS work...
1753-
1754- // The . has its id_expr
1755- assert (n.ops [0 ].id_expr );
1756-
1757- // The ( has its expr_list and op_close
1758- assert (n.ops [1 ].expr_list && n.ops [1 ].op_close );
1759-
1760- // --------------------------------------------------------------------
1761- // TODO: When MSVC supports __VA_OPT__ in standard mode without the
1762- // experimental /Zc:preprocessor switch, use this single line
1763- // instead of the dual lines below that special-case _0 args
1764- // AND: Make the similarly noted change in cpp2util.h
1765- //
1766- // printer.print_cpp2("CPP2_UFCS(", n.position());
1767-
1768- auto ufcs_string = std::string (" CPP2_UFCS" );
1769- if (n.ops [0 ].id_expr ->template_args_count () > 0 ) {
1770- ufcs_string += " _TEMPLATE" ;
1771- }
1772- // If there are no additional arguments, use the _0 version
1773- if (n.ops [1 ].expr_list ->expressions .empty ()) {
1774- ufcs_string += " _0" ;
1775- }
1776- printer.print_cpp2 (ufcs_string+" (" , n.position ());
1777- // --------------------------------------------------------------------
1778-
1779- // Make the "funcname" the first argument to CPP2_UFCS
1780- emit (*n.ops [0 ].id_expr );
1781- printer.print_cpp2 (" , " , n.position ());
1782-
1783- // Then make the base expression the second argument
1784- emit (*n.expr );
1785-
1786- // Then tack on any additional arguments
1787- if (!n.ops [1 ].expr_list ->expressions .empty ()) {
1788- printer.print_cpp2 (" , " , n.position ());
1789- push_need_expression_list_parens (false );
1790- emit (*n.ops [1 ].expr_list );
1791- pop_need_expression_list_parens ();
1792- }
1793- printer.print_cpp2 (" )" , n.position ());
1794-
1795- // And we're done. This path has handled this node, so return...
1796- return ;
1797- }
1798-
17991728 // Otherwise, we're going to have to potentially do some work to change
18001729 // some Cpp2 postfix operators to Cpp1 prefix operators, so let's set up...
18011730 auto prefix = std::vector<text_with_pos>{};
@@ -1804,6 +1733,31 @@ class cppfront
18041733 auto last_was_prefixed = false ;
18051734 auto saw_dollar = false ;
18061735
1736+ struct text_chunks_with_parens_position {
1737+ std::vector<text_with_pos> text_chunks;
1738+ cpp2::source_position open_pos;
1739+ cpp2::source_position close_pos;
1740+ };
1741+
1742+ auto args = std::optional<text_chunks_with_parens_position>{};
1743+
1744+ auto print_to_string = [&](auto & i, auto ... args) {
1745+ auto print = std::string{};
1746+ printer.emit_to_string (&print);
1747+ emit (i, args...);
1748+ printer.emit_to_string ();
1749+ return print;
1750+ };
1751+ auto print_to_text_chunks = [&](auto & i, auto ... args) {
1752+ auto text = std::vector<text_with_pos>{};
1753+ printer.emit_to_text_chunks (&text);
1754+ push_need_expression_list_parens (false );
1755+ emit (i, args...);
1756+ pop_need_expression_list_parens ();
1757+ printer.emit_to_text_chunks ();
1758+ return text;
1759+ };
1760+
18071761 for (auto i = n.ops .rbegin (); i != n.ops .rend (); ++i)
18081762 {
18091763 assert (i->op );
@@ -1827,9 +1781,64 @@ class cppfront
18271781 }
18281782 }
18291783
1784+ // Going backwards if we found LeftParen it might be UFCS
1785+ // expr_list is emited to args variable for future use
1786+ if (i->op ->type () == lexeme::LeftParen) {
1787+
1788+ assert (i->op );
1789+ assert (i->op_close );
1790+ auto local_args = text_chunks_with_parens_position{{}, i->op ->position (), i->op_close ->position ()};
1791+
1792+ if (!i->expr_list ->expressions .empty ()) {
1793+ local_args.text_chunks = print_to_text_chunks (*i->expr_list );
1794+ }
1795+
1796+ args.emplace (std::move (local_args));
1797+ }
1798+ // Going backwards if we found Dot and there is args variable
1799+ // it means that it should be handled by UFCS
1800+ else if ( i->op ->type () == lexeme::Dot && args )
1801+ {
1802+ auto funcname = print_to_string (*i->id_expr );
1803+
1804+ // --------------------------------------------------------------------
1805+ // TODO: When MSVC supports __VA_OPT__ in standard mode without the
1806+ // experimental /Zc:preprocessor switch, use this single line
1807+ // instead of the dual lines below that special-case _0 args
1808+ // AND: Make the similarly noted change in cpp2util.h
1809+ //
1810+ // printer.print_cpp2("CPP2_UFCS(", n.position());
1811+
1812+ auto ufcs_string = std::string (" CPP2_UFCS" );
1813+
1814+ if (i->id_expr ->template_args_count () > 0 ) {
1815+ ufcs_string += " _TEMPLATE" ;
1816+ // we need to replace "fun<int,long,double>" to "fun, (<int,long,double>)" to be able to generate
1817+ // from obj.fun<int, long, double>(1,2) this CPP2_UFCS_TEMPLATE(fun, (<int,long, double>), obj, 1, 2)
1818+ auto split = funcname.find (' <' ); assert (split != std::string::npos);
1819+ funcname.insert (split, " , (" );
1820+ assert (funcname.back () == ' >' );
1821+ funcname += ' )' ;
1822+ }
1823+ // If there are no additional arguments, use the _0 version
1824+ if (args.value ().text_chunks .empty ()) {
1825+ ufcs_string += " _0" ;
1826+ }
1827+
1828+ prefix.emplace_back (ufcs_string + " (" + funcname + " , " , i->op ->position () );
1829+ suffix.emplace_back (" )" , args.value ().close_pos );
1830+ if (!args.value ().text_chunks .empty ()) {
1831+ for (auto && e: args.value ().text_chunks ) {
1832+ suffix.push_back (e);
1833+ }
1834+ suffix.emplace_back (" , " , i->op ->position ());
1835+ }
1836+ args.reset ();
1837+ }
1838+
18301839 // Handle the Cpp2 postfix operators that are prefix in Cpp1
18311840 //
1832- if (i->op ->type () == lexeme::MinusMinus ||
1841+ else if (i->op ->type () == lexeme::MinusMinus ||
18331842 i->op ->type () == lexeme::PlusPlus ||
18341843 i->op ->type () == lexeme::Multiply ||
18351844 i->op ->type () == lexeme::Ampersand ||
@@ -1873,21 +1882,25 @@ class cppfront
18731882 }
18741883
18751884 if (i->id_expr ) {
1876- auto print = std::string{};
1877- printer.emit_to_string (&print);
1878- emit (*i->id_expr , false /* not a local name*/ );
1879- printer.emit_to_string ();
1885+
1886+ if (args) {
1887+ // if args are stored it means that this is function or method
1888+ // that is not handled by UFCS and args need to be printed
1889+ suffix.emplace_back (" )" , args.value ().close_pos );
1890+ for (auto && e: args.value ().text_chunks ) {
1891+ suffix.push_back (e);
1892+ }
1893+ suffix.emplace_back (" (" , args.value ().open_pos );
1894+ args.reset ();
1895+ }
1896+
1897+ auto print = print_to_string (*i->id_expr , false /* not a local name*/ );
18801898 suffix.emplace_back ( print, i->id_expr ->position () );
18811899 }
18821900
18831901 if (i->expr_list ) {
1884- auto text = std::vector<text_with_pos>{};
1885- printer.emit_to_text_chunks (&text);
1886- push_need_expression_list_parens (false );
1887- emit (*i->expr_list );
1888- pop_need_expression_list_parens ();
1889- printer.emit_to_text_chunks ();
1890- for (auto && e: text) {
1902+ auto text = print_to_text_chunks (*i->expr_list );
1903+ for (auto && e: text) {
18911904 suffix.push_back (e);
18921905 }
18931906 }
@@ -1903,6 +1916,8 @@ class cppfront
19031916 }
19041917 }
19051918
1919+
1920+
19061921 // Print the prefixes (in forward order)
19071922 for (auto & e : prefix) {
19081923 printer.print_cpp2 (e.text , n.position ());
@@ -1927,6 +1942,18 @@ class cppfront
19271942 }
19281943 suppress_move_from_last_use = false ;
19291944
1945+ if (args) {
1946+ // if after printing core expression args is defined
1947+ // it means that the chaining started by function call
1948+ // we need to print its arguments
1949+ suffix.emplace_back (" )" , args.value ().close_pos );
1950+ for (auto && e: args.value ().text_chunks ) {
1951+ suffix.push_back (e);
1952+ }
1953+ suffix.emplace_back (" (" , args.value ().open_pos );
1954+ args.reset ();
1955+ }
1956+
19301957 // Print the suffixes (in reverse order)
19311958 while (!suffix.empty ()) {
19321959 printer.print_cpp2 (suffix.back ().text , suffix.back ().pos );
0 commit comments