48 changes: 32 additions & 16 deletions source/cppfront.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2038,7 +2038,7 @@ class cppfront
if (*n.identifier == "while") {
assert(
n.condition
&& n.statement
&& n.statements
&& !n.range
&& !n.body
);
Expand All @@ -2059,7 +2059,7 @@ class cppfront
if (!labelname.empty()) {
printer.print_extra("{");
}
emit(*n.statement);
emit(*n.statements);
if (!labelname.empty()) {
printer.print_extra(" CPP2_CONTINUE_BREAK("+labelname+") }");
}
Expand All @@ -2072,7 +2072,7 @@ class cppfront
else if (*n.identifier == "do") {
assert(
n.condition
&& n.statement
&& n.statements
&& !n.range
&& !n.body
);
Expand All @@ -2081,7 +2081,7 @@ class cppfront
if (!labelname.empty()) {
printer.print_extra("{");
}
emit(*n.statement);
emit(*n.statements);
if (!labelname.empty()) {
printer.print_extra(" CPP2_CONTINUE_BREAK("+labelname+") }");
}
Expand All @@ -2104,8 +2104,9 @@ class cppfront
else if (*n.identifier == "for") {
assert(
!n.condition
&& !n.statement
&& !n.statements
&& n.range
&& n.parameter
&& n.body
);

Expand All @@ -2117,7 +2118,7 @@ class cppfront
}
emit(*n.range);
printer.print_cpp2("; ", n.position());
emit(*n.get_for_parameter());
emit(*n.parameter);
printer.print_cpp2(" : cpp2_range ) ", n.position());
if (!labelname.empty()) {
printer.print_extra("{");
Expand All @@ -2130,8 +2131,8 @@ class cppfront
printer.print_cpp2(" { do ", n.position());
}

assert(n.body->initializer);
emit(*n.body->initializer);
assert(n.body);
emit(*n.body);

if (n.next_expression) {
printer.print_cpp2(" while (false); ", n.position());
Expand Down Expand Up @@ -3616,7 +3617,21 @@ class cppfront
)
-> void
{
// Do expression statement case first... it's the most comple
auto emit_parameters =
!n.emitted
&& n.parameters
;

if (emit_parameters) {
printer.print_extra( "\n");
printer.print_extra( "{");
for (auto& param : n.parameters->parameters) {
printer.print_extra( "\n");
printer.print_extra( print_to_string(*param) );
}
}

// Do expression statement case first... it's the most complex
// because it's used for single-statement function bodies
try_emit<statement_node::expression >(
n.statement,
Expand Down Expand Up @@ -3657,6 +3672,11 @@ class cppfront
try_emit<statement_node::jump >(n.statement);

printer.preempt_position_pop();

if (emit_parameters) {
printer.print_extra( "\n");
printer.print_extra( "}");
}
}


Expand Down Expand Up @@ -3793,10 +3813,6 @@ class cppfront
// Else handle ordinary parameters

auto unqid = std::get_if<type_id_node::unqualified>(&type_id.id);
auto is_wildcard =
unqid
&& *(*unqid)->identifier == "_"
;

// If this parameter's name is an unqualified-id, check to see
// if it's the name of one of the template parameters
Expand Down Expand Up @@ -3826,7 +3842,7 @@ class cppfront
// First any prefix
if (
!is_returns
&& !is_wildcard
&& !type_id.is_wildcard()
&& !is_template_parameter_name
)
{
Expand All @@ -3840,7 +3856,7 @@ class cppfront
printer.preempt_position_push( n.position() );

if (
is_wildcard
type_id.is_wildcard()
|| is_template_parameter_name
)
{
Expand Down Expand Up @@ -3895,7 +3911,7 @@ class cppfront
// Then any suffix
if (
!is_returns
&& !is_wildcard
&& !type_id.is_wildcard()
&& !is_template_parameter_name
)
{
Expand Down
143 changes: 92 additions & 51 deletions source/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -1201,13 +1201,11 @@ struct iteration_statement_node
token const* identifier = {};
std::unique_ptr<assignment_expression_node> next_expression; // if used, else null
std::unique_ptr<logical_or_expression_node> condition; // used for "do" and "while", else null
std::unique_ptr<compound_statement_node> statement; // used for "do" and "while", else null
std::unique_ptr<compound_statement_node> statements; // used for "do" and "while", else null
std::unique_ptr<expression_node> range; // used for "for", else null
std::unique_ptr<declaration_node> body; // used for "for", else null
bool for_with_in = false;// usse for "for," says whether loop variable is 'in'

auto get_for_parameter() const
-> parameter_declaration_node const*;
std::unique_ptr<parameter_declaration_node> parameter; // used for "for", else null
std::unique_ptr<statement_node> body; // used for "for", else null
bool for_with_in = false;// used for "for," says whether loop variable is 'in'

auto position() const
-> source_position
Expand Down Expand Up @@ -1382,6 +1380,8 @@ struct parameter_declaration_list_node;

struct statement_node
{
std::unique_ptr<parameter_declaration_list_node> parameters;

enum active { expression=0, compound, selection, declaration, return_, iteration, contract, inspect, jump };
std::variant<
std::unique_ptr<expression_statement_node>,
Expand Down Expand Up @@ -3025,20 +3025,6 @@ auto primary_expression_node::visit(auto& v, int depth)
}


auto iteration_statement_node::get_for_parameter() const
-> parameter_declaration_node const*
{
assert(*identifier == "for");
auto func = std::get_if<declaration_node::a_function>(&body->type);
assert(
func
&& *func
&& std::ssize((**func).parameters->parameters) == 1
);
return (**func).parameters->parameters[0].get();
}


auto iteration_statement_node::visit(auto& v, int depth)
-> void
{
Expand All @@ -3049,8 +3035,8 @@ auto iteration_statement_node::visit(auto& v, int depth)
if (identifier) {
identifier->visit(v, depth+1);
}
if (statement) {
statement->visit(v, depth+1);
if (statements) {
statements->visit(v, depth+1);
}
if (next_expression) {
next_expression->visit(v, depth+1);
Expand All @@ -3060,8 +3046,9 @@ auto iteration_statement_node::visit(auto& v, int depth)
condition->visit(v, depth+1);
}
else {
assert(range && body);
assert(range && parameter && body);
range->visit(v, depth+1);
parameter->visit(v, depth+1);
body->visit(v, depth+1);
}
v.end(*this, depth);
Expand Down Expand Up @@ -4740,7 +4727,9 @@ class parser
{
auto n = std::make_unique<iteration_statement_node>();

// If the next three tokens are: identifier ':' 'for/while/do', it's a labeled iteration statement
// If the next three tokens are:
// identifier ':' 'for/while/do'
// then it's a labeled iteration statement
if (
curr().type() == lexeme::Identifier
&& peek(1)
Expand Down Expand Up @@ -4799,7 +4788,7 @@ class parser
error("invalid while loop body", true, {}, true);
return false;
}
n->statement= std::move(s);
n->statements = std::move(s);
return true;
};
//-----------------------------------------------------------------
Expand Down Expand Up @@ -4851,29 +4840,42 @@ class parser

if (!handle_optional_next_clause()) { return {}; }

if (curr() != "do") {
error("'for condition' must be followed by 'do'");
return {};
}
next();

n->body = unnamed_declaration(curr().position());
auto func = n->body ? std::get_if<declaration_node::a_function>(&n->body->type) : nullptr;
if (
!n->body
|| n->body->identifier
|| !func
|| !*func
|| std::ssize((**func).parameters->parameters) != 1
|| (**func).returns.index() != function_type_node::empty
curr() != "do"
|| !peek(1)
|| peek(1)->type() != lexeme::LeftParen
)
{
error("for..do loop body must be an unnamed function taking a single parameter and returning nothing", false);
next();
if (curr().type() == lexeme::Colon) {
error("alpha design change note: 'for range' syntax has changed - please remove ':' and '=', for example: for args do (arg) std::cout << arg;");
}
else {
error("'for range' must be followed by 'do ( parameter )'");
}
return {};
}
next(2); // eat 'do' and '('

n->parameter = parameter_declaration(false, false, false);
if (!n->parameter) {
error("'for range do (' must be followed by a parameter declaration", false, source_position{}, true);
return {};
}

if (curr().type() != lexeme::RightParen) {
error("expected ')' after 'for' parameter");
return {};
}
next(); // eat ')'

n->body = statement();
if (!n->body) {
error("invalid for..do loop body", false, source_position{}, true);
return {};
}
// else
assert(func && *func);
if ((**func).parameters->parameters.front()->pass == passing_style::in) {
if (n->parameter->pass == passing_style::in) {
n->for_with_in = true;
}

Expand Down Expand Up @@ -5100,7 +5102,8 @@ class parser
//G
auto statement(
bool semicolon_required = true,
source_position equal_sign = source_position{}
source_position equal_sign = source_position{},
bool parameters_allowed = false
)
-> std::unique_ptr<statement_node>
{
Expand All @@ -5111,6 +5114,23 @@ class parser

auto n = std::make_unique<statement_node>();

// If a parameter list is allowed here, try to parse one
if (parameters_allowed) {
n->parameters = parameter_declaration_list(false, true, false, true);
if (n->parameters) {
for (auto& param : n->parameters->parameters) {
if (
param->direction() != passing_style::in
&& param->direction() != passing_style::inout
)
{
error("parameters scoped to a block/statement must be 'in' (the default) or 'inout'", false);
return {};
}
}
}
}

// Now handle the rest of the statement

if (auto s = selection_statement()) {
Expand Down Expand Up @@ -5244,7 +5264,9 @@ class parser
return {};
}

auto s = statement(true);
// Only inside a compound-statement, a
// contained statement() may have parameters
auto s = statement(true, source_position{}, true);
if (!s) {
pos = start_pos; // backtrack
return {};
Expand Down Expand Up @@ -5274,9 +5296,10 @@ class parser
//G 'final'
//G
auto parameter_declaration(
bool is_returns = false,
bool is_named = true,
bool is_template = true
bool is_returns = false,
bool is_named = true,
bool is_template = true,
bool is_statement = false
)
-> std::unique_ptr<parameter_declaration_node>
{
Expand Down Expand Up @@ -5404,6 +5427,7 @@ class parser

if (
!is_returns
&& !is_statement
&& n->declaration->initializer
)
{
Expand All @@ -5425,10 +5449,17 @@ class parser
auto parameter_declaration_list(
bool is_returns = false,
bool is_named = true,
bool is_template = false
bool is_template = false,
bool is_statement = false
)
-> std::unique_ptr<parameter_declaration_list_node>
{
// Remember current position, because we need to look ahead in
// the case of seeing whether a local statement starts with a
// parameter list, since finding that it doesn't (it's some other
// parenthesized expression) is not an error, just backtrack
auto start_pos = pos;

auto opener = lexeme::LeftParen;
auto closer = lexeme::RightParen;
if (is_template) {
Expand All @@ -5446,7 +5477,7 @@ class parser

auto param = std::make_unique<parameter_declaration_node>();

while ((param = parameter_declaration(is_returns, is_named, is_template)) != nullptr)
while ((param = parameter_declaration(is_returns, is_named, is_template, is_statement)) != nullptr)
{
if (
std::ssize(n->parameters) > 1
Expand All @@ -5462,14 +5493,24 @@ class parser
break;
}
else if (curr().type() != lexeme::Comma) {
error("expected , in parameter list", true, {}, true);
if (is_statement) {
pos = start_pos; // backtrack
}
else {
error("expected , in parameter list", true, {}, true);
}
return {};
}
next();
}

if (curr().type() != closer) {
error("invalid parameter list", true, {}, true);
if (is_statement) {
pos = start_pos; // backtrack
}
else {
error("invalid parameter list", true, {}, true);
}
next();
return {};
}
Expand Down
16 changes: 16 additions & 0 deletions source/sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,7 @@ class sema
bool is_out_expression = false;
bool inside_parameter_list = false;
bool inside_returns_list = false;
bool just_entered_for = false;
parameter_declaration_node const* inside_out_parameter = {};

auto start(parameter_declaration_list_node const&, int) -> void
Expand Down Expand Up @@ -1366,8 +1367,23 @@ class sema
inside_returns_list = false;
}

auto start(iteration_statement_node const& n, int) -> void
{
if (*n.identifier == "for") {
just_entered_for = true;
}
}

auto start(declaration_node const& n, int) -> void
{
// Skip the first declaration after entering a 'for',
// which is the for loop parameter - it's always
// guaranteed to be initialized by the language
if (just_entered_for) {
just_entered_for = false;
return;
}

if (
!n.is_alias()
// Skip type scope (member) variables
Expand Down