summaryrefslogtreecommitdiffhomepage
path: root/CompilerDriver/cc2/source/parse.h
diff options
context:
space:
mode:
authorAmlal El Mahrouss <amlal.elmahrouss@icloud.com>2023-12-30 23:39:37 +0100
committerAmlal El Mahrouss <amlal.elmahrouss@icloud.com>2023-12-30 23:39:37 +0100
commit263915832993dd12beee10e204f9ebcc6c786ed2 (patch)
tree862e51208a99c35746e574a76564a4532b3a4a49 /CompilerDriver/cc2/source/parse.h
Meta: initial commit of WestCo optimized toolchain.
Signed-off-by: Amlal El Mahrouss <amlal.elmahrouss@icloud.com>
Diffstat (limited to 'CompilerDriver/cc2/source/parse.h')
-rw-r--r--CompilerDriver/cc2/source/parse.h9263
1 files changed, 9263 insertions, 0 deletions
diff --git a/CompilerDriver/cc2/source/parse.h b/CompilerDriver/cc2/source/parse.h
new file mode 100644
index 0000000..e8e6daf
--- /dev/null
+++ b/CompilerDriver/cc2/source/parse.h
@@ -0,0 +1,9263 @@
+
+// Copyright (c) Herb Sutter
+// SPDX-License-Identifier: CC-BY-NC-ND-4.0
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+
+//===========================================================================
+// Parser
+//===========================================================================
+
+#ifndef CPP2_PARSE_H
+#define CPP2_PARSE_H
+
+#include "lex.h"
+#include <memory>
+#include <variant>
+#include <iostream>
+
+
+namespace cpp2 {
+
+auto violates_lifetime_safety = false;
+
+//-----------------------------------------------------------------------
+// Operator categorization
+//
+
+//G prefix-operator:
+//G one of '!' '-' '+'
+//GT parameter-direction
+//G
+auto is_prefix_operator(token const& tok)
+ -> bool
+{
+ //if (to_passing_style(tok) != passing_style::invalid) {
+ // return true;
+ //}
+
+ switch (tok.type()) {
+ break;case lexeme::Not:
+ case lexeme::Minus:
+ case lexeme::Plus:
+ return true;
+ break;default:
+ return false;
+ }
+}
+
+
+//G postfix-operator:
+//G one of '++' '--' '*' '&' '~' '$' '...'
+//G
+auto is_postfix_operator(lexeme l)
+ -> bool
+{
+ switch (l) {
+ break;case lexeme::PlusPlus:
+ case lexeme::MinusMinus:
+ case lexeme::Multiply:
+ case lexeme::Ampersand:
+ case lexeme::Tilde:
+ case lexeme::Dollar:
+ case lexeme::Ellipsis:
+ return true;
+ break;default:
+ return false;
+ }
+}
+
+
+//G assignment-operator:
+//G one of '=' '*=' '/=' '%=' '+=' '-=' '>>=' '<<=' '&=' '^=' '|='
+//G
+auto is_assignment_operator(lexeme l)
+ -> bool
+{
+ switch (l) {
+ break;case lexeme::Assignment:
+ case lexeme::MultiplyEq:
+ case lexeme::SlashEq:
+ case lexeme::ModuloEq:
+ case lexeme::PlusEq:
+ case lexeme::MinusEq:
+ case lexeme::RightShiftEq:
+ case lexeme::LeftShiftEq:
+ case lexeme::AmpersandEq:
+ case lexeme::CaretEq:
+ case lexeme::PipeEq:
+ return true;
+ break;default:
+ return false;
+ }
+}
+
+
+//-----------------------------------------------------------------------
+//
+// Parse tree node types
+//
+//-----------------------------------------------------------------------
+//
+
+//-----------------------------------------------------------------------
+// try_visit
+//
+// Helper to visit whatever is in a variant where each
+// alternative is a smart pointer
+//
+template <int I>
+auto try_visit(auto& variant, auto& visitor, int depth)
+ -> void
+{
+ if (variant.index() == I) {
+ auto const& s = std::get<I>(variant);
+ assert (s);
+ s->visit(visitor, depth+1);
+ }
+}
+
+
+struct expression_list_node;
+struct id_expression_node;
+struct declaration_node;
+struct inspect_expression_node;
+struct literal_node;
+struct template_argument;
+
+
+struct primary_expression_node
+{
+ enum active { empty=0, identifier, expression_list, id_expression, declaration, inspect, literal };
+ std::variant<
+ std::monostate,
+ token const*,
+ std::unique_ptr<expression_list_node>,
+ std::unique_ptr<id_expression_node>,
+ std::unique_ptr<declaration_node>,
+ std::unique_ptr<inspect_expression_node>,
+ std::unique_ptr<literal_node>
+ > expr;
+ // Cache to work around <https://github.com/llvm/llvm-project/issues/73336>.
+ bool expression_list_is_fold_expression = false;
+
+
+ // API
+ //
+ auto is_fold_expression() const
+ -> bool;
+
+ auto is_identifier() const
+ -> bool;
+
+ auto is_id_expression() const
+ -> bool;
+
+ auto is_expression_list() const
+ -> bool;
+
+ auto get_expression_list() const
+ -> expression_list_node const*;
+
+ auto is_literal() const
+ -> bool;
+
+ auto template_arguments() const -> std::vector<template_argument> const&;
+
+ auto get_token() const -> token const*;
+
+ auto to_string() const
+ -> std::string;
+
+ // Internals
+ //
+ auto position() const -> source_position;
+ auto visit(auto& v, int depth) -> void;
+};
+
+
+struct literal_node {
+ token const* literal = {};
+ token const* user_defined_suffix = {};
+
+ // API
+ //
+ auto get_token() const
+ -> token const*
+ {
+ return literal;
+ }
+
+ auto to_string() const
+ -> std::string
+ {
+ assert (literal);
+ auto ret = literal->to_string();
+ if (user_defined_suffix) {
+ ret += user_defined_suffix->to_string();
+ }
+ return ret;
+ }
+
+ // Internals
+ //
+ auto position() const
+ -> source_position
+ {
+ assert (literal);
+ return literal->position();
+ }
+
+ auto visit(auto& v, int depth) -> void
+ {
+ v.start(*this, depth);
+ assert (literal);
+ literal->visit(v, depth+1);
+ if (user_defined_suffix) {
+ user_defined_suffix->visit(v, depth+1);
+ }
+ v.end(*this, depth);
+ }
+};
+
+
+struct postfix_expression_node;
+
+struct prefix_expression_node
+{
+ std::vector<token const*> ops;
+ std::unique_ptr<postfix_expression_node> expr;
+
+ // API
+ //
+ auto is_fold_expression() const
+ -> bool;
+
+ auto is_identifier() const
+ -> bool;
+
+ auto is_id_expression() const
+ -> bool;
+
+ auto is_expression_list() const
+ -> bool;
+
+ auto get_expression_list() const
+ -> expression_list_node const*;
+
+ auto get_postfix_expression_node() const
+ -> postfix_expression_node *
+ {
+ assert(expr);
+ return expr.get();
+ }
+
+ auto is_literal() const
+ -> bool;
+
+ auto is_result_a_temporary_variable() const -> bool;
+
+ auto to_string() const
+ -> std::string;
+
+ // Internals
+ //
+ auto position() const -> source_position;
+ auto visit(auto& v, int depth) -> void;
+};
+
+
+struct expression_node;
+
+
+template<
+ String Name,
+ typename Term
+>
+struct binary_expression_node
+{
+ std::unique_ptr<Term> expr;
+ expression_node const* my_expression = {};
+
+ binary_expression_node();
+
+ struct term
+ {
+ token const* op;
+ std::unique_ptr<Term> expr;
+ };
+ std::vector<term> terms;
+
+
+ // API
+ //
+ auto is_fold_expression() const
+ -> bool
+ {
+ // This is a fold-expression if any subexpression
+ // has an identifier named "..."
+ auto ret = expr->is_fold_expression();
+ for (auto& x : terms) {
+ ret |= x.expr->is_fold_expression();
+ }
+ return ret;
+ }
+
+ auto lhs_is_id_expression() const
+ -> bool
+ {
+ return expr->is_id_expression();
+ }
+
+ auto is_standalone_expression() const
+ -> bool;
+
+ auto terms_size() const
+ -> int
+ {
+ return std::ssize(terms);
+ }
+
+ auto is_identifier() const
+ -> bool
+ {
+ return terms.empty() && expr->is_identifier();
+ }
+
+ auto is_id_expression() const
+ -> bool
+ {
+ return terms.empty() && expr->is_id_expression();
+ }
+
+ auto is_expression_list() const
+ -> bool
+ {
+ return terms.empty() && expr->is_expression_list();
+ }
+
+ auto get_expression_list() const
+ -> expression_list_node const*
+ {
+ if (is_expression_list()) {
+ return expr->get_expression_list();
+ }
+ return {};
+ }
+
+ auto is_literal() const
+ -> bool
+ {
+ return terms.empty() && expr->is_literal();
+ }
+
+ // Get left-hand postfix-expression
+ auto get_postfix_expression_node() const
+ -> postfix_expression_node *
+ {
+ assert(expr);
+ return expr->get_postfix_expression_node();
+ }
+
+ // Get first right-hand postfix-expression, if there is one
+ auto get_second_postfix_expression_node() const
+ -> postfix_expression_node *
+ {
+ if (!terms.empty()) {
+ assert(terms.front().expr);
+ return terms.front().expr->get_postfix_expression_node();
+ }
+ // else
+ return {};
+ }
+
+ // "Simple" means binary (size>0) and not chained (size<2)
+ struct get_lhs_rhs_if_simple_binary_expression_with_ret {
+ postfix_expression_node* lhs;
+ Term* rhs;
+ };
+ auto get_lhs_rhs_if_simple_binary_expression_with(lexeme op) const
+ -> get_lhs_rhs_if_simple_binary_expression_with_ret
+ {
+ if (
+ std::ssize(terms) == 1
+ && terms[0].op->type() == op
+ )
+ {
+ return {
+ get_postfix_expression_node(),
+ terms.front().expr.get()
+ };
+ }
+ // Else
+ return { nullptr, nullptr };
+ }
+
+ auto is_result_a_temporary_variable() const -> bool {
+ if constexpr (std::string_view(Name.value) == "assignment") {
+ assert(expr);
+ return expr->is_result_a_temporary_variable();
+ } else {
+ if (terms.empty()) {
+ assert(expr);
+ return expr->is_result_a_temporary_variable();
+ } else {
+ return true;
+ }
+ }
+ }
+
+ auto to_string() const
+ -> std::string
+ {
+ assert (expr);
+ auto ret = expr->to_string();
+ for (auto const& x : terms) {
+ assert (x.op);
+ ret += " " + x.op->to_string();
+ assert (x.expr);
+ ret += " " + x.expr->to_string();
+ }
+ return ret;
+ }
+
+
+ // Internals
+ //
+ auto position() const
+ -> source_position
+ {
+ assert (expr);
+ return expr->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ assert (expr);
+ expr->visit(v, depth+1);
+ for (auto const& x : terms) {
+ assert (x.op);
+ v.start(*x.op, depth+1);
+ assert (x.expr);
+ x.expr->visit(v, depth+1);
+ }
+ v.end(*this, depth);
+ }
+};
+
+
+struct is_as_expression_node;
+
+using multiplicative_expression_node = binary_expression_node< "multiplicative" , is_as_expression_node >;
+using additive_expression_node = binary_expression_node< "additive" , multiplicative_expression_node >;
+using shift_expression_node = binary_expression_node< "shift" , additive_expression_node >;
+using compare_expression_node = binary_expression_node< "compare" , shift_expression_node >;
+using relational_expression_node = binary_expression_node< "relational" , compare_expression_node >;
+using equality_expression_node = binary_expression_node< "equality" , relational_expression_node >;
+using bit_and_expression_node = binary_expression_node< "bit-and" , equality_expression_node >;
+using bit_xor_expression_node = binary_expression_node< "bit-xor" , bit_and_expression_node >;
+using bit_or_expression_node = binary_expression_node< "bit-or" , bit_xor_expression_node >;
+using logical_and_expression_node = binary_expression_node< "logical-and" , bit_or_expression_node >;
+using logical_or_expression_node = binary_expression_node< "logical-or" , logical_and_expression_node >;
+using assignment_expression_node = binary_expression_node< "assignment" , logical_or_expression_node >;
+
+
+struct assignment_expression_lhs_rhs {
+ postfix_expression_node* lhs;
+ logical_or_expression_node* rhs;
+};
+
+
+struct expression_statement_node;
+
+struct expression_node
+{
+ static inline std::vector<expression_node*> current_expressions = {};
+
+ std::unique_ptr<assignment_expression_node> expr;
+ int num_subexpressions = 0;
+ expression_statement_node const* my_statement = {};
+
+ expression_node();
+
+ // API
+ //
+ auto is_fold_expression() const
+ -> bool
+ {
+ // This is a fold-expression if any subexpression
+ // has an identifier named "..."
+ return expr->is_fold_expression();
+ }
+
+ auto is_standalone_expression() const
+ -> bool;
+
+ auto subexpression_count() const
+ -> int
+ {
+ return num_subexpressions;
+ }
+
+ auto is_identifier() const
+ -> bool
+ {
+ return expr->is_identifier();
+ }
+
+ auto is_id_expression() const
+ -> bool
+ {
+ return expr->is_id_expression();
+ }
+
+ auto is_expression_list() const
+ -> bool
+ {
+ return expr->is_expression_list();
+ }
+
+ auto get_expression_list() const
+ -> expression_list_node const*
+ {
+ if (is_expression_list()) {
+ return expr->get_expression_list();
+ }
+ return {};
+ }
+
+ auto is_literal() const
+ -> bool
+ {
+ return expr->is_literal();
+ }
+
+ auto get_lhs_rhs_if_simple_assignment() const
+ -> assignment_expression_lhs_rhs;
+
+ auto to_string() const
+ -> std::string
+ {
+ assert (expr);
+ return expr->to_string();
+ }
+
+ // Internals
+ //
+ auto position() const -> source_position
+ {
+ assert (expr);
+ return expr->position();
+ }
+
+ auto visit(auto& v, int depth) -> void
+ {
+ v.start(*this, depth);
+ assert (expr);
+ expr->visit(v, depth+1);
+ v.end(*this, depth);
+ }
+};
+
+
+template<
+ String Name,
+ typename Term
+>
+binary_expression_node<Name, Term>::binary_expression_node() {
+ if (!expression_node::current_expressions.empty()) {
+ my_expression = expression_node::current_expressions.back();
+ }
+}
+
+
+template<
+ String Name,
+ typename Term
+>
+auto binary_expression_node<Name, Term>::is_standalone_expression() const
+ -> bool
+{
+ return
+ my_expression
+ && my_expression->is_standalone_expression()
+ ;
+}
+
+
+enum class passing_style { in=0, copy, inout, out, move, forward, invalid };
+auto to_passing_style(token const& t) -> passing_style {
+ if (t.type() == lexeme::Identifier) {
+ if (t == "in" ) { return passing_style::in; }
+ if (t == "copy" ) { return passing_style::copy; }
+ if (t == "inout" ) { return passing_style::inout; }
+ if (t == "out" ) { return passing_style::out; }
+ if (t == "move" ) { return passing_style::move; }
+ if (t == "forward") { return passing_style::forward; }
+ }
+ return passing_style::invalid;
+}
+auto to_string_view(passing_style pass) -> std::string_view {
+ switch (pass) {
+ break;case passing_style::in : return "in";
+ break;case passing_style::copy : return "copy";
+ break;case passing_style::inout : return "inout";
+ break;case passing_style::out : return "out";
+ break;case passing_style::move : return "move";
+ break;case passing_style::forward: return "forward";
+ break;default : return "INVALID passing_style";
+ }
+}
+
+
+struct expression_list_node
+{
+ token const* open_paren = {};
+ token const* close_paren = {};
+ bool inside_initializer = false;
+
+ struct term {
+ passing_style pass = {};
+ std::unique_ptr<expression_node> expr;
+
+ auto visit(auto& v, int depth) -> void
+ {
+ v.start(*this, depth);
+ assert(expr);
+ expr->visit(v, depth+1);
+ v.end(*this, depth);
+ }
+ };
+ std::vector< term > expressions;
+
+
+ // API
+ //
+ auto is_fold_expression() const
+ -> bool
+ {
+ // This is a fold-expression if any subexpression
+ // has an identifier named "..."
+ auto ret = false;
+ for (auto& x : expressions) {
+ ret |= x.expr->is_fold_expression();
+ }
+ return ret;
+ }
+
+
+ // Internals
+ //
+ auto position() const
+ -> source_position
+ {
+ // Make sure this got set
+ assert (open_paren);
+ return open_paren->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ for (auto& x : expressions) {
+ x.visit(v, depth+1);
+ }
+ v.end(*this, depth);
+ }
+};
+
+auto primary_expression_node::is_identifier() const
+ -> bool
+{
+ return expr.index() == identifier;
+}
+
+auto primary_expression_node::is_id_expression() const
+ -> bool
+{
+ return expr.index() == id_expression;
+}
+
+auto primary_expression_node::is_expression_list() const
+ -> bool
+{
+ return expr.index() == expression_list;
+}
+
+auto primary_expression_node::get_expression_list() const
+ -> expression_list_node const*
+{
+ if (is_expression_list()) {
+ return std::get<expression_list>(expr).get();
+ }
+ return {};
+}
+
+auto primary_expression_node::is_literal() const
+ -> bool
+{
+ return expr.index() == literal;
+}
+
+
+struct expression_statement_node
+{
+ static inline std::vector<expression_statement_node*> current_expression_statements = {};
+
+ std::unique_ptr<expression_node> expr;
+ bool has_semicolon = false;
+
+ // API
+ //
+ auto subexpression_count() const
+ -> int
+ {
+ assert (expr);
+ return expr->subexpression_count();
+ }
+
+ auto to_string() const
+ -> std::string
+ {
+ assert (expr);
+ return expr->to_string();
+ }
+
+ // Internals
+ //
+ auto position() const
+ -> source_position
+ {
+ assert (expr);
+ return expr->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ assert (expr);
+ expr->visit(v, depth+1);
+ v.end(*this, depth);
+ }
+};
+
+
+auto expression_node::is_standalone_expression() const
+ -> bool
+{
+ return
+ my_statement
+ && my_statement->subexpression_count() == subexpression_count()
+ ;
+}
+
+
+struct capture {
+ postfix_expression_node* capture_expr;
+ std::string cap_sym = {};
+ std::string str = {};
+ std::string str_suppressed_move = {};
+ auto operator==(postfix_expression_node* p) { return capture_expr == p; }
+};
+
+struct capture_group {
+ std::vector<capture> members;
+
+ auto add(postfix_expression_node* p)
+ -> void
+ {
+ members.push_back({p});
+ }
+
+ auto remove(postfix_expression_node* p)
+ -> void;
+
+ ~capture_group();
+};
+
+
+struct postfix_expression_node
+{
+ std::unique_ptr<primary_expression_node> expr;
+
+ struct term
+ {
+ token const* op;
+
+ // This is used if *op is . - can be null
+ std::unique_ptr<id_expression_node> id_expr = {};
+
+ // These are used if *op is [ or ( - can be null
+ std::unique_ptr<expression_list_node> expr_list = {};
+ token const* op_close = {};
+ };
+ std::vector<term> ops;
+ capture_group* cap_grp = {};
+
+ ~postfix_expression_node();
+
+ // API
+ //
+ auto is_fold_expression() const
+ -> bool
+ {
+ // This is a fold-expression if any subexpression
+ // has an identifier named "..."
+ return expr->is_fold_expression();
+ }
+
+ auto is_identifier() const
+ -> bool
+ {
+ return ops.empty() && expr->is_identifier();
+ }
+
+ auto is_id_expression() const
+ -> bool
+ {
+ return ops.empty() && expr->is_id_expression();
+ }
+
+ auto is_expression_list() const
+ -> bool
+ {
+ return ops.empty() && expr->is_expression_list();
+ }
+
+ auto get_expression_list() const
+ -> expression_list_node const*
+ {
+ if (is_expression_list()) {
+ return expr->get_expression_list();
+ }
+ return {};
+ }
+
+ auto is_literal() const
+ -> bool
+ {
+ return ops.empty() && expr->is_literal();
+ }
+
+ auto get_first_token_ignoring_this() const
+ -> token const*;
+
+ auto is_result_a_temporary_variable() const -> bool {
+ if (ops.empty()) {
+ return false;
+ } else {
+ return (ops.front().op->type() == lexeme::Ampersand
+ || ops.front().op->type() == lexeme::Tilde);
+ }
+ }
+
+ auto to_string() const
+ -> std::string;
+
+ // Internals
+ //
+ auto position() const -> source_position
+ {
+ assert (expr);
+ return expr->position();
+ }
+
+ auto visit(auto& v, int depth) -> void;
+};
+
+auto prefix_expression_node::is_fold_expression() const
+ -> bool
+{
+ // This is a fold-expression if any subexpression
+ // has an identifier named "..."
+ return expr->is_fold_expression();
+}
+
+auto prefix_expression_node::is_identifier() const
+ -> bool
+{
+ return ops.empty() && expr->is_identifier();
+}
+
+auto prefix_expression_node::is_id_expression() const
+ -> bool
+{
+ return ops.empty() && expr->is_id_expression();
+}
+
+auto prefix_expression_node::is_expression_list() const
+ -> bool
+{
+ return ops.empty() && expr->is_expression_list();
+}
+
+auto prefix_expression_node::get_expression_list() const
+ -> expression_list_node const*
+{
+ if (is_expression_list()) {
+ return expr->get_expression_list();
+ }
+ return {};
+}
+
+auto prefix_expression_node::is_literal() const
+ -> bool
+{
+ return ops.empty() && expr->is_literal();
+}
+
+auto prefix_expression_node::is_result_a_temporary_variable() const -> bool {
+ if (ops.empty()) {
+ return expr->is_result_a_temporary_variable();
+ } else {
+ return true;
+ }
+}
+
+
+auto expression_node::get_lhs_rhs_if_simple_assignment() const
+ -> assignment_expression_lhs_rhs
+{
+ auto ret = expr->get_lhs_rhs_if_simple_binary_expression_with(lexeme::Assignment);
+ return { ret.lhs, ret.rhs };
+}
+
+
+auto capture_group::remove(postfix_expression_node* p)
+ -> void
+{
+ p->cap_grp = {};
+ auto old_size = members.size();
+ std::erase(members, p);
+ assert (members.size() == old_size-1);
+}
+
+
+capture_group::~capture_group()
+{
+ assert (members.empty());
+ // We shouldn't need to do this:
+ // while (!members.empty()) {
+ // remove(members.front().capture_expr);
+ // }
+ // if the capture_group outlives the tree of things that can point to it
+ // => each node with a capture_group should declare it as the first member
+ // before any other node that could own a postfix_expression that could
+ // point back up to that capture_group
+}
+
+
+auto prefix_expression_node::to_string() const
+ -> std::string
+{
+ auto ret = std::string{};
+
+ for (auto const& x : ops) {
+ assert (x);
+ ret += x->as_string_view();
+ }
+
+ assert (expr);
+ return ret + expr->to_string();
+}
+
+
+auto prefix_expression_node::position() const
+ -> source_position
+{
+ if (std::ssize(ops) > 0) {
+ return ops.front()->position();
+ }
+ assert (expr);
+ return expr->position();
+}
+
+
+auto prefix_expression_node::visit(auto& v, int depth)
+ -> void
+{
+ v.start(*this, depth);
+ for (auto const& x : ops) {
+ assert (x);
+ v.start(*x, depth+1);
+ }
+ assert (expr);
+ expr->visit(v, depth+1);
+ v.end(*this, depth);
+}
+
+
+struct type_id_node;
+struct template_args_tag { };
+
+struct template_argument
+{
+ enum active { empty=0, expression, type_id };
+ source_position comma;
+ std::variant<
+ std::monostate,
+ std::unique_ptr<expression_node>,
+ std::unique_ptr<type_id_node>
+ > arg;
+
+ auto to_string() const
+ -> std::string;
+};
+
+// Used by functions that must return a reference to an empty arg list
+inline std::vector<template_argument> const no_template_args;
+
+struct unqualified_id_node
+{
+ token const* identifier = {}; // required
+
+ // These are used only if it's a template-id
+ source_position open_angle = {};
+ source_position close_angle = {};
+
+ std::vector<template_argument> template_args;
+
+ auto template_arguments() const
+ -> std::vector<template_argument> const&
+ {
+ return template_args;
+ }
+
+ auto get_token() const
+ -> token const*
+ {
+ if (open_angle == source_position{}) {
+ assert (identifier);
+ return identifier;
+ }
+ // else
+ return {};
+ }
+
+ auto to_string() const
+ -> std::string;
+
+ auto position() const
+ -> source_position
+ {
+ assert (identifier);
+ return identifier->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ assert (identifier);
+ v.start(*identifier, depth+1);
+
+ if (open_angle != source_position{}) {
+ // Inform the visitor that this is a template args list
+ v.start(template_args_tag{}, depth);
+ assert(open_angle != source_position{});
+ assert(close_angle != source_position{});
+ assert(template_args.empty()
+ || template_args.front().comma == source_position{});
+ for (auto& a : template_args) {
+ try_visit<template_argument::expression>(a.arg, v, depth+1);
+ try_visit<template_argument::type_id >(a.arg, v, depth+1);
+ }
+ v.end(template_args_tag{}, depth);
+ }
+
+ v.end(*this, depth);
+ }
+};
+
+
+struct qualified_id_node
+{
+ struct term {
+ token const* scope_op;
+ std::unique_ptr<unqualified_id_node> id = {};
+
+ term( token const* o ) : scope_op{o} { }
+ };
+ std::vector<term> ids;
+
+ auto template_arguments() const
+ -> std::vector<template_argument> const&
+ {
+ return ids.back().id->template_arguments();
+ }
+
+ auto get_token() const
+ -> token const*
+ {
+ if (
+ std::ssize(ids) == 1
+ && !ids.front().scope_op
+ )
+ {
+ assert (ids.front().id);
+ return ids.front().id->get_token();
+ }
+ // else
+ return {};
+ }
+
+ auto to_string() const
+ -> std::string
+ {
+ auto ret = std::string{};
+ for (auto& term : ids) {
+ if (term.scope_op) {
+ ret += term.scope_op->as_string_view();
+ }
+ assert (term.id);
+ ret += term.id->to_string();
+ }
+ return ret;
+ }
+
+ auto get_first_token() const
+ -> token const*
+ {
+ assert (
+ !ids.empty()
+ && ids.front().id
+ );
+ return ids.front().id->get_token();
+ }
+
+ auto position() const
+ -> source_position
+ {
+ assert (!ids.empty());
+ if (ids.front().scope_op) {
+ return ids.front().scope_op->position();
+ }
+ else {
+ assert (ids.front().id);
+ return ids.front().id->position();
+ }
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ for (auto const& x : ids) {
+ if (x.scope_op) {
+ x.scope_op->visit(v, depth+1);
+ }
+ assert(x.id);
+ x.id->visit(v, depth+1);
+ }
+ v.end(*this, depth);
+ }
+};
+
+
+struct type_id_node
+{
+ source_position pos;
+
+ std::vector<token const*> pc_qualifiers;
+ token const* address_of = {};
+ token const* dereference_of = {};
+ int dereference_cnt = {};
+ token const* suspicious_initialization = {};
+
+ enum active { empty=0, qualified, unqualified, keyword };
+ std::variant<
+ std::monostate,
+ std::unique_ptr<qualified_id_node>,
+ std::unique_ptr<unqualified_id_node>,
+ token const*
+ > id;
+
+ auto is_wildcard() const
+ -> bool
+ {
+ return
+ id.index() == type_id_node::empty
+ || (get_token() && *get_token() == "_")
+ ;
+ }
+
+ auto is_pointer_qualified() const
+ -> bool
+ {
+ for (auto q : pc_qualifiers) {
+ if (q->type() == lexeme::Multiply) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ auto is_concept() const
+ -> bool
+ {
+ auto tok = get_token();
+ return tok && *tok == "concept";
+ }
+
+ auto template_arguments() const
+ -> std::vector<template_argument> const&
+ {
+ if (id.index() == unqualified) {
+ return std::get<unqualified>(id)->template_arguments();
+ }
+ // else
+ return std::get<qualified>(id)->template_arguments();
+ }
+
+ auto to_string() const
+ -> std::string
+ {
+ switch (id.index()) {
+ break;case empty:
+ return {};
+ break;case qualified:
+ return std::get<qualified>(id)->to_string();
+ break;case unqualified:
+ return std::get<unqualified>(id)->to_string();
+ break;case keyword:
+ return std::get<keyword>(id)->to_string();
+ break;default:
+ assert(!"ICE: invalid type_id state");
+ }
+ // else
+ return {};
+ }
+
+ auto get_token() const
+ -> token const*
+ {
+ switch (id.index()) {
+ break;case empty:
+ return {};
+ break;case qualified:
+ return {};
+ break;case unqualified:
+ return get<unqualified>(id)->get_token();
+ break;case keyword:
+ return get<keyword>(id);
+ break;default:
+ assert(!"ICE: invalid type_id state");
+ }
+ // else
+ return {};
+ }
+
+ auto position() const
+ -> source_position
+ {
+ return pos;
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ for (auto q : pc_qualifiers) {
+ v.start(*q, depth+1);
+ }
+ try_visit<qualified >(id, v, depth);
+ try_visit<unqualified>(id, v, depth);
+ try_visit<keyword >(id, v, depth);
+ v.end(*this, depth);
+ }
+};
+
+auto unqualified_id_node::to_string() const
+ -> std::string
+{
+ assert(identifier);
+ auto ret = identifier->to_string();
+ if (open_angle != source_position{}) {
+ auto separator = std::string{"<"};
+ for (auto& t : template_args) {
+ ret += separator;
+ assert(t.arg.index() != template_argument::empty);
+ if (t.arg.index() == template_argument::expression) {
+ ret += std::get<template_argument::expression>(t.arg)->to_string();
+ }
+ else if (t.arg.index() == template_argument::type_id) {
+ ret += std::get<template_argument::type_id>(t.arg)->to_string();
+ }
+ separator = ",";
+ }
+ if (std::ssize(template_args) > 0) {
+ ret += ">";
+ }
+ }
+ return ret;
+}
+
+auto template_argument::to_string() const
+ -> std::string
+{
+ switch (arg.index()) {
+ break;case empty:
+ return {};
+ break;case expression:
+ return std::get<expression>(arg)->to_string();
+ break;case type_id:
+ return std::get<type_id>(arg)->to_string();
+ break;default:
+ assert(!"ICE: invalid template_argument state");
+ }
+ // else
+ return {};
+}
+
+
+struct is_as_expression_node
+{
+ std::unique_ptr<prefix_expression_node> expr;
+
+ struct term
+ {
+ token const* op = {};
+
+ // This is used if *op is a type - can be null
+ std::unique_ptr<type_id_node> type = {};
+
+ // This is used if *op is an expression - can be null
+ std::unique_ptr<expression_node> expr = {};
+ };
+ std::vector<term> ops;
+
+
+ // API
+ //
+ auto is_fold_expression() const
+ -> bool
+ {
+ // This is a fold-expression if any subexpression
+ // has an identifier named "..."
+ return expr->is_fold_expression();
+ }
+
+ auto is_identifier() const
+ -> bool
+ {
+ return ops.empty() && expr->is_identifier();
+ }
+
+ auto is_id_expression() const
+ -> bool
+ {
+ return ops.empty() && expr->is_id_expression();
+ }
+
+ auto is_expression_list() const
+ -> bool
+ {
+ return ops.empty() && expr->is_expression_list();
+ }
+
+ auto get_expression_list() const
+ -> expression_list_node const*
+ {
+ if (is_expression_list()) {
+ return expr->get_expression_list();
+ }
+ return {};
+ }
+
+ auto is_literal() const
+ -> bool
+ {
+ return ops.empty() && expr->is_literal();
+ }
+
+ auto get_postfix_expression_node() const
+ -> postfix_expression_node *
+ {
+ assert(expr);
+ return expr->get_postfix_expression_node();
+ }
+
+ auto is_result_a_temporary_variable() const -> bool {
+ if (ops.empty()) {
+ assert(expr);
+ return expr->is_result_a_temporary_variable();
+ } else {
+ return true;
+ }
+ }
+
+ auto to_string() const
+ -> std::string
+ {
+ assert (expr);
+ auto ret = expr->to_string();
+ for (auto const& x : ops) {
+ assert (x.op);
+ ret += " " + x.op->to_string();
+ if (x.type) {
+ ret += " " + x.type->to_string();
+ }
+ if (x.expr) {
+ ret += " " + x.expr->to_string();
+ }
+ }
+ return ret;
+ }
+
+ // Internals
+ //
+ auto position() const
+ -> source_position
+ {
+ assert (expr);
+ return expr->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ assert (expr);
+ expr->visit(v, depth+1);
+ for (auto const& x : ops) {
+ assert (x.op);
+ v.start(*x.op, depth+1);
+ if (x.type) {
+ x.type->visit(v, depth+1);
+ }
+ if (x.expr) {
+ x.expr->visit(v, depth+1);
+ }
+ }
+ v.end(*this, depth);
+ }
+};
+
+
+expression_node::expression_node()
+{
+ if (!expression_statement_node::current_expression_statements.empty()) {
+ my_statement = expression_statement_node::current_expression_statements.back();
+ }
+}
+
+
+struct id_expression_node
+{
+ source_position pos;
+
+ enum active { empty=0, qualified, unqualified };
+ std::variant<
+ std::monostate,
+ std::unique_ptr<qualified_id_node>,
+ std::unique_ptr<unqualified_id_node>
+ > id;
+
+ auto template_arguments() const
+ -> std::vector<template_argument> const&
+ {
+ if (is_unqualified()) {
+ return std::get<unqualified>(id)->template_arguments();
+ }
+ // else
+ return std::get<qualified>(id)->template_arguments();
+ }
+
+ auto is_fold_expression() const
+ -> bool
+ {
+ // This is a fold-expression if any subexpression has
+ // has an identifier named "..."
+ auto tok = get_token();
+ return tok && *tok == "...";
+ }
+
+ auto is_empty() const
+ -> bool
+ {
+ return id.index() == empty;
+ }
+
+ auto is_qualified() const
+ -> bool
+ {
+ return id.index() == qualified;
+ }
+
+ auto is_unqualified() const
+ -> bool
+ {
+ return id.index() == unqualified;
+ }
+
+ auto get_token() const
+ -> token const*
+ {
+ if (id.index() == unqualified) {
+ return std::get<unqualified>(id)->get_token();
+ }
+ // else
+ return {};
+ }
+
+ auto to_string() const
+ -> std::string
+ {
+ if (id.index() == qualified) {
+ return std::get<qualified>(id)->to_string();
+ }
+ else if (id.index() == unqualified) {
+ return std::get<unqualified>(id)->to_string();
+ }
+ // else
+ return {};
+ }
+
+ auto position() const
+ -> source_position
+ {
+ return pos;
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ try_visit<qualified >(id, v, depth);
+ try_visit<unqualified>(id, v, depth);
+ v.end(*this, depth);
+ }
+};
+
+
+postfix_expression_node::~postfix_expression_node()
+{
+ if (cap_grp) {
+ cap_grp->remove(this);
+ }
+}
+
+
+auto primary_expression_node::is_fold_expression() const
+ -> bool
+{
+ // This is a fold-expression if any subexpression has
+ // has an identifier named "..."
+ switch (expr.index()) {
+ break;case identifier:
+ return *std::get<identifier>(expr) == "...";
+ break;case expression_list:
+ return expression_list_is_fold_expression;
+ break;case id_expression:
+ return std::get<id_expression>(expr)->is_fold_expression();
+ break;default: ; // the others can't contain folds
+ }
+ return false;
+}
+
+
+auto postfix_expression_node::get_first_token_ignoring_this() const
+ -> token const*
+{
+ if (
+ expr->get_token()
+ && *expr->get_token() == "this"
+ && std::ssize(ops) == 1
+ && ops[0].op->type() == lexeme::Dot
+ )
+ {
+ return ops[0].id_expr->get_token();
+ }
+ return expr->get_token();
+}
+
+
+auto postfix_expression_node::to_string() const
+ -> std::string
+{
+ assert (expr);
+ auto ret = expr->to_string();
+
+ for (auto const& x : ops) {
+ assert (x.op);
+ ret += x.op->as_string_view();
+ if (x.id_expr) {
+ ret += x.id_expr->to_string();
+ }
+ if (x.expr_list) {
+ return "(*ERROR*) temporary alpha limitation: type metafunctions cannot stringize expressions that involve nested expression-lists, declarations, or inspect expressions";
+ }
+ }
+
+ return ret;
+}
+
+
+auto postfix_expression_node::visit(auto& v, int depth)
+ -> void
+{
+ v.start(*this, depth);
+ assert (expr);
+ expr->visit(v, depth+1);
+ for (auto const& x : ops) {
+ assert (x.op);
+ v.start(*x.op, depth+1);
+ if (x.id_expr) {
+ x.id_expr->visit(v, depth+1);
+ }
+ if (x.expr_list) {
+ x.expr_list->visit(v, depth+1);
+ }
+ }
+ v.end(*this, depth);
+}
+
+
+struct statement_node;
+
+struct compound_statement_node
+{
+ source_position open_brace;
+ source_position close_brace;
+ std::vector<std::unique_ptr<statement_node>> statements;
+
+ colno_t body_indent = 0;
+
+ compound_statement_node(source_position o = source_position{});
+
+ auto position() const
+ -> source_position
+ {
+ return open_brace;
+ }
+
+ auto visit(auto& v, int depth) -> void;
+};
+
+
+struct selection_statement_node
+{
+ bool is_constexpr = false;
+ token const* identifier = {};
+ source_position else_pos;
+ std::unique_ptr<logical_or_expression_node> expression;
+ std::unique_ptr<compound_statement_node> true_branch;
+ std::unique_ptr<compound_statement_node> false_branch;
+ bool has_source_false_branch = false;
+
+ auto position() const
+ -> source_position
+ {
+ assert (identifier);
+ return identifier->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ assert (identifier);
+ v.start(*identifier, depth+1);
+ assert (expression);
+ expression->visit(v, depth+1);
+ assert (true_branch);
+ true_branch->visit(v, depth+1);
+ if (false_branch) {
+ false_branch->visit(v, depth+1);
+ }
+ v.end(*this, depth);
+ }
+};
+
+
+struct parameter_declaration_node;
+
+struct iteration_statement_node
+{
+ token const* label = {};
+ 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> statements; // used for "do" and "while", else null
+ std::unique_ptr<expression_node> range; // used for "for", else null
+ 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
+ {
+ if (label) {
+ return label->position();
+ }
+ assert(identifier);
+ return identifier->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void;
+};
+
+
+struct return_statement_node
+{
+ token const* identifier = {};
+ std::unique_ptr<expression_node> expression;
+
+ auto position() const
+ -> source_position
+ {
+ assert(identifier);
+ return identifier->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ if (expression) {
+ expression->visit(v, depth+1);
+ }
+ v.end(*this, depth);
+ }
+};
+
+
+struct alternative_node
+{
+ std::unique_ptr<unqualified_id_node> name;
+ token const* is_as_keyword = {};
+
+ // One of these will be used
+ std::unique_ptr<type_id_node> type_id;
+ std::unique_ptr<postfix_expression_node> value;
+
+ source_position equal_sign;
+ std::unique_ptr<statement_node> statement;
+
+ auto position() const
+ -> source_position
+ {
+ assert(is_as_keyword);
+ return is_as_keyword->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void;
+};
+
+
+struct inspect_expression_node
+{
+ bool is_constexpr = false;
+ token const* identifier = {};
+ std::unique_ptr<expression_node> expression;
+ std::unique_ptr<type_id_node> result_type;
+ source_position open_brace;
+ source_position close_brace;
+
+ std::vector<std::unique_ptr<alternative_node>> alternatives;
+
+ auto position() const
+ -> source_position
+ {
+ assert(identifier);
+ return identifier->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ assert (identifier);
+ v.start(*identifier, depth+1);
+ assert (expression);
+ expression->visit(v, depth+1);
+ if (result_type) {
+ result_type->visit(v, depth+1);
+ }
+ for (auto&& alt : alternatives) {
+ alt->visit(v, depth+1);
+ }
+ v.end(*this, depth);
+ }
+};
+
+
+struct contract_node
+{
+ // Declared first, because it should outlive any owned
+ // postfix_expressions that could refer to it
+ capture_group captures;
+
+ source_position open_bracket;
+ token const* kind = {};
+ std::unique_ptr<id_expression_node> group;
+ std::vector<std::unique_ptr<id_expression_node>> flags;
+ std::unique_ptr<logical_or_expression_node> condition;
+ std::unique_ptr<expression_node> message = {};
+
+ contract_node( source_position pos )
+ : open_bracket{pos}
+ { }
+
+ auto position() const
+ -> source_position
+ {
+ return open_bracket;
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+
+ assert(kind);
+ kind->visit(v, depth+1);
+
+ if (group) {
+ group->visit(v, depth+1);
+ }
+
+ for (auto const& f : flags) {
+ f->visit(v, depth+1);
+ }
+
+ assert(condition);
+ condition->visit(v, depth+1);
+
+ if (message) {
+ message->visit(v, depth+1);
+ }
+
+ v.end(*this, depth);
+ }
+};
+
+
+struct jump_statement_node
+{
+ token const* keyword;
+ token const* label;
+
+ auto position() const
+ -> source_position
+ {
+ assert(keyword);
+ return keyword->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ if (keyword) {
+ keyword->visit(v, depth+1);
+ }
+ if (label) {
+ label->visit(v, depth+1);
+ }
+ v.end(*this, depth);
+ }
+};
+
+
+struct using_statement_node
+{
+ token const* keyword = {};
+ bool for_namespace = false;
+ std::unique_ptr<id_expression_node> id;
+
+ auto position() const
+ -> source_position
+ {
+ assert(keyword);
+ return keyword->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ assert(id);
+ id->visit(v, depth+1);
+ v.end(*this, depth);
+ }
+};
+
+
+struct parameter_declaration_list_node;
+
+struct statement_node
+{
+ std::unique_ptr<parameter_declaration_list_node> parameters;
+ compound_statement_node* compound_parent = nullptr;
+
+ statement_node(compound_statement_node* compound_parent_ = nullptr);
+
+ enum active { expression=0, compound, selection, declaration, return_, iteration, using_, contract, inspect, jump };
+ std::variant<
+ std::unique_ptr<expression_statement_node>,
+ std::unique_ptr<compound_statement_node>,
+ std::unique_ptr<selection_statement_node>,
+ std::unique_ptr<declaration_node>,
+ std::unique_ptr<return_statement_node>,
+ std::unique_ptr<iteration_statement_node>,
+ std::unique_ptr<using_statement_node>,
+ std::unique_ptr<contract_node>,
+ std::unique_ptr<inspect_expression_node>,
+ std::unique_ptr<jump_statement_node>
+ > statement;
+
+ bool emitted = false; // a note field that's used during lowering to Cpp1
+
+ bool marked_for_removal = false; // for use during metafunctions which may replace members
+
+ // API
+ //
+ auto is_expression () const -> bool { return statement.index() == expression; }
+ auto is_compound () const -> bool { return statement.index() == compound; }
+ auto is_selection () const -> bool { return statement.index() == selection; }
+ auto is_declaration() const -> bool { return statement.index() == declaration; }
+ auto is_return () const -> bool { return statement.index() == return_; }
+ auto is_iteration () const -> bool { return statement.index() == iteration; }
+ auto is_using () const -> bool { return statement.index() == using_; }
+ auto is_contract () const -> bool { return statement.index() == contract; }
+ auto is_inspect () const -> bool { return statement.index() == inspect; }
+ auto is_jump () const -> bool { return statement.index() == jump; }
+
+ template<typename Node>
+ auto get_if()
+ -> Node*
+ {
+ auto pnode = std::get_if<std::unique_ptr<Node>>(&statement);
+ if (pnode) {
+ return pnode->get();
+ }
+ // else
+ return nullptr;
+ }
+
+ template<typename Node>
+ auto get_if() const
+ -> Node const*
+ {
+ auto pnode = std::get_if<std::unique_ptr<Node>>(&statement);
+ if (pnode) {
+ return pnode->get();
+ }
+ // else
+ return nullptr;
+ }
+
+ auto get_lhs_rhs_if_simple_assignment() const
+ -> assignment_expression_lhs_rhs
+ {
+ if (is_expression()) {
+ return std::get<expression>(statement)->expr->get_lhs_rhs_if_simple_assignment();
+ }
+ // Else
+ return {};
+ }
+
+ auto to_string() const
+ -> std::string
+ {
+ switch (statement.index()) {
+ break;case expression:
+ return std::get<expression>(statement)->to_string();
+ break;default:
+ return "(*ERROR*) temporary alpha limitation: type metafunctions cannot stringize expressions that involve initializer statements other than expression-statements";
+ }
+ }
+
+ // Internals
+ //
+ auto position() const
+ -> source_position;
+
+ auto visit(auto& v, int depth)
+ -> void;
+};
+
+
+auto alternative_node::visit(auto& v, int depth)
+ -> void
+{
+ v.start(*this, depth);
+ if (name) {
+ v.start(*name, depth+1);
+ }
+ assert (is_as_keyword);
+ v.start(*is_as_keyword, depth+1);
+ if (type_id) {
+ type_id->visit(v, depth+1);
+ }
+ else {
+ assert (value);
+ value->visit(v, depth+1);
+ }
+ assert (statement);
+ statement->visit(v, depth+1);
+ v.end(*this, depth);
+}
+
+
+auto compound_statement_node::visit(auto& v, int depth)
+ -> void
+{
+ v.start(*this, depth);
+ for (auto const& x : statements) {
+ assert(x);
+ x->visit(v, depth+1);
+ }
+ v.end(*this, depth);
+}
+
+
+struct parameter_declaration_node
+{
+ source_position pos = {};
+ passing_style pass = passing_style::in;
+ int ordinal = 1;
+
+ enum class modifier { none=0, implicit, virtual_, override_, final_ };
+ modifier mod = modifier::none;
+
+ std::unique_ptr<declaration_node> declaration;
+
+ // API
+ //
+ auto has_name() const
+ -> bool;
+
+ auto name() const
+ -> token const*;
+
+ auto has_name(std::string_view) const
+ -> bool;
+
+ auto direction() const
+ -> passing_style
+ {
+ return pass;
+ }
+
+ auto is_implicit() const
+ -> bool
+ {
+ return mod == modifier::implicit;
+ }
+
+ auto is_virtual() const
+ -> bool
+ {
+ return mod == modifier::virtual_;
+ }
+
+ auto make_virtual()
+ -> void
+ {
+ mod = modifier::virtual_;
+ }
+
+ auto is_override() const
+ -> bool
+ {
+ return mod == modifier::override_;
+ }
+
+ auto is_final() const
+ -> bool
+ {
+ return mod == modifier::final_;
+ }
+
+ auto is_polymorphic() const
+ -> bool
+ {
+ switch (mod) {
+ break;case modifier::virtual_:
+ case modifier::override_:
+ case modifier::final_:
+ return true;
+ break;default:
+ return false;
+ }
+ }
+
+ // Internals
+ //
+ auto position() const
+ -> source_position;
+
+ auto visit(auto& v, int depth)
+ -> void;
+};
+
+
+struct parameter_declaration_list_node
+{
+ token const* open_paren = {};
+ token const* close_paren = {};
+
+ std::vector<std::unique_ptr<parameter_declaration_node>> parameters;
+
+ // API
+ //
+ auto ssize() const -> auto {
+ return std::ssize(parameters);
+ }
+
+ auto operator[](int i)
+ -> parameter_declaration_node*
+ {
+ return parameters[i].get();
+ }
+
+ auto operator[](int i) const
+ -> parameter_declaration_node const*
+ {
+ return parameters[i].get();
+ }
+
+ // Internals
+ //
+ auto position() const
+ -> source_position
+ {
+ assert(open_paren);
+ return open_paren->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ for (auto const& x : parameters) {
+ assert(x);
+ x->visit(v, depth+1);
+ }
+ v.end(*this, depth);
+ }
+};
+
+
+auto statement_node::visit(auto& v, int depth)
+ -> void
+{
+ v.start(*this, depth);
+ if (parameters) {
+ parameters->visit(v, depth+1);
+ }
+ try_visit<expression >(statement, v, depth);
+ try_visit<compound >(statement, v, depth);
+ try_visit<selection >(statement, v, depth);
+ try_visit<declaration>(statement, v, depth);
+ try_visit<return_ >(statement, v, depth);
+ try_visit<iteration >(statement, v, depth);
+ try_visit<contract >(statement, v, depth);
+ try_visit<inspect >(statement, v, depth);
+ try_visit<jump >(statement, v, depth);
+ v.end(*this, depth);
+}
+
+
+struct function_returns_tag { };
+
+struct function_type_node
+{
+ declaration_node* my_decl;
+
+ std::unique_ptr<parameter_declaration_list_node> parameters;
+ bool throws = false;
+
+ struct single_type_id {
+ std::unique_ptr<type_id_node> type;
+ passing_style pass = passing_style::move;
+ };
+
+ enum active { empty = 0, id, list };
+ std::variant<
+ std::monostate,
+ single_type_id,
+ std::unique_ptr<parameter_declaration_list_node>
+ > returns;
+
+ std::vector<std::unique_ptr<contract_node>> contracts;
+
+ function_type_node(declaration_node* decl);
+
+ // API
+ //
+ auto has_postconditions() const
+ -> bool;
+
+ auto is_function_with_this() const
+ -> bool;
+
+ auto is_virtual_function() const
+ -> bool;
+
+ auto make_function_virtual()
+ -> bool;
+
+ auto is_defaultable() const
+ -> bool;
+
+ auto is_constructor() const
+ -> bool;
+
+ auto is_default_constructor() const
+ -> bool;
+
+ auto is_move() const
+ -> bool;
+
+ auto is_swap() const
+ -> bool;
+
+ auto is_constructor_with_that() const
+ -> bool;
+
+ auto is_constructor_with_in_that() const
+ -> bool;
+
+ auto is_constructor_with_move_that() const
+ -> bool;
+
+ auto is_comparison() const
+ -> bool;
+
+ auto is_increment_or_decrement() const
+ -> bool;
+
+ auto is_compound_assignment() const
+ -> bool;
+
+ auto is_assignment() const
+ -> bool;
+
+ auto is_assignment_with_that() const
+ -> bool;
+
+ auto is_assignment_with_in_that() const
+ -> bool;
+
+ auto is_assignment_with_move_that() const
+ -> bool;
+
+ auto is_destructor() const
+ -> bool;
+
+ auto has_declared_return_type() const
+ -> bool
+ {
+ return returns.index() != empty;
+ }
+
+ auto has_deduced_return_type() const
+ -> bool
+ {
+ return
+ returns.index() == empty
+ || (
+ returns.index() == id
+ && std::get<function_type_node::id>(returns).type->is_wildcard()
+ )
+ ;
+ }
+
+ auto unnamed_return_type_to_string() const
+ -> std::string
+ {
+ if (auto id = std::get_if<function_type_node::id>(&returns)) {
+ return (*id).type->to_string();
+ }
+ return {};
+ }
+
+ auto has_bool_return_type() const
+ -> bool
+ {
+ if (auto id = std::get_if<function_type_node::id>(&returns)) {
+ if (auto name = (*id).type->get_token()) {
+ return *name == "bool";
+ }
+ }
+ return false;
+ }
+
+ auto has_non_void_return_type() const
+ -> bool
+ {
+ if (auto id = std::get_if<function_type_node::id>(&returns)) {
+ if (auto name = (*id).type->get_token()) {
+ return *name != "void";
+ }
+ }
+ return returns.index() != empty;
+ }
+
+ auto parameter_count() const
+ -> int
+ {
+ return std::ssize(parameters->parameters);
+ }
+
+ auto index_of_parameter_named(std::string_view s) const
+ -> int
+ {
+ auto ret = 0;
+ for (auto& param : parameters->parameters) {
+ if (param->has_name(s)) {
+ return ret;
+ }
+ ++ret;
+ }
+ return -1;
+ }
+
+ auto has_parameter_named(std::string_view s) const
+ -> bool
+ {
+ for (auto& param : parameters->parameters) {
+ if (param->has_name(s)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ auto has_parameter_with_name_and_pass(
+ std::string_view s,
+ passing_style pass
+ ) const
+ -> bool
+ {
+ for (auto& param : parameters->parameters) {
+ if (
+ param->has_name(s)
+ && param->pass == pass
+ )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ auto first_parameter_name() const
+ -> std::string;
+
+ auto nth_parameter_type_name(int n) const
+ -> std::string;
+
+ auto has_in_parameter_named(std::string_view s) const
+ -> bool
+ {
+ return has_parameter_with_name_and_pass(s, passing_style::in);
+ }
+
+ auto has_out_parameter_named(std::string_view s) const
+ -> bool
+ {
+ return has_parameter_with_name_and_pass(s, passing_style::out);
+ }
+
+ auto has_move_parameter_named(std::string_view s) const
+ -> bool
+ {
+ return has_parameter_with_name_and_pass(s, passing_style::move);
+ }
+
+ // Internals
+ //
+ auto position() const
+ -> source_position
+ {
+ assert (parameters);
+ return parameters->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ assert(parameters);
+ parameters->visit(v, depth+1);
+
+ if (returns.index() == id) {
+ auto& r = std::get<id>(returns);
+ assert(r.type);
+ r.type->visit(v, depth+1);
+ }
+ else if (returns.index() == list) {
+ auto& r = std::get<list>(returns);
+ assert(r);
+ // Inform the visitor that this is a returns list
+ v.start(function_returns_tag{}, depth);
+ r->visit(v, depth+1);
+ v.end(function_returns_tag{}, depth);
+ }
+ v.end(*this, depth);
+ }
+};
+
+
+struct type_node
+{
+ token const* type;
+ bool final = false;
+
+ type_node(
+ token const* t,
+ bool final_ = false
+ )
+ : type{t}
+ , final{final_}
+ { }
+
+ // API
+ //
+ auto is_final() const
+ -> bool
+ {
+ return final;
+ }
+
+ auto make_final()
+ -> void
+ {
+ final = true;
+ }
+
+ // Internals
+ //
+ auto position() const
+ -> source_position
+ {
+ assert(type);
+ return type->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ v.end(*this, depth);
+ }
+};
+
+
+struct namespace_node
+{
+ token const* namespace_;
+
+ namespace_node(token const* ns) : namespace_{ns} { }
+
+ auto position() const
+ -> source_position
+ {
+ assert(namespace_);
+ return namespace_->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+ v.end(*this, depth);
+ }
+};
+
+
+struct alias_node
+{
+ token const* type = {};
+ std::unique_ptr<type_id_node> type_id; // for objects
+
+ enum active : std::uint8_t { a_type, a_namespace, an_object };
+ std::variant<
+ std::unique_ptr<type_id_node>,
+ std::unique_ptr<id_expression_node>,
+ std::unique_ptr<expression_node>
+ > initializer;
+
+ alias_node( token const* t ) : type{t} { }
+
+ // API
+ //
+ auto is_type_alias () const -> bool
+ { return initializer.index() == a_type; }
+ auto is_namespace_alias() const -> bool
+ { return initializer.index() == a_namespace; }
+ auto is_object_alias () const -> bool
+ { return initializer.index() == an_object; }
+
+ // Internals
+ //
+ auto position() const
+ -> source_position
+ {
+ assert (type);
+ return type->position();
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+
+ try_visit<a_type >(initializer, v, depth+1);
+ try_visit<a_namespace>(initializer, v, depth+1);
+ try_visit<an_object >(initializer, v, depth+1);
+
+ v.end(*this, depth);
+ }
+};
+
+
+enum class accessibility { default_ = 0, public_, protected_, private_ };
+
+auto to_string(accessibility a)
+ -> std::string
+{
+ switch (a) {
+ break;case accessibility::public_ : return "public";
+ break;case accessibility::protected_: return "protected";
+ break;case accessibility::private_ : return "private";
+ break;default: assert(a == accessibility::default_);
+ }
+ return "default";
+}
+
+
+struct declaration_identifier_tag { };
+
+struct declaration_node
+{
+ // The capture_group is declared first, because it should outlive
+ // any owned postfix_expressions that could refer to it
+ capture_group captures;
+ source_position pos;
+ bool is_variadic = false;
+ bool is_constexpr = false;
+ bool terse_no_equals = false;
+ std::unique_ptr<unqualified_id_node> identifier;
+ accessibility access = accessibility::default_;
+
+ enum active : std::uint8_t { a_function, an_object, a_type, a_namespace, an_alias };
+ std::variant<
+ std::unique_ptr<function_type_node>,
+ std::unique_ptr<type_id_node>,
+ std::unique_ptr<type_node>,
+ std::unique_ptr<namespace_node>,
+ std::unique_ptr<alias_node>
+ > type;
+
+ std::vector<std::unique_ptr<id_expression_node>> metafunctions;
+ std::unique_ptr<parameter_declaration_list_node> template_parameters;
+ source_position requires_pos = {};
+ std::unique_ptr<logical_or_expression_node> requires_clause_expression;
+
+ source_position equal_sign = {};
+ std::unique_ptr<statement_node> initializer;
+
+ declaration_node* parent_declaration = {};
+ statement_node* my_statement = {};
+
+ // Attributes currently configurable only via metafunction API,
+ // not directly in the base language grammar
+ bool member_function_generation = true;
+
+ // Cache some context
+ bool is_template_parameter = false;
+ bool is_parameter = false;
+
+ // Constructor
+ //
+ declaration_node(declaration_node* parent)
+ : parent_declaration{parent}
+ { }
+
+ // API
+ //
+ auto type_member_mark_for_removal()
+ -> bool
+ {
+ if (my_statement) {
+ my_statement->marked_for_removal = true;
+ return true;
+ }
+ return false;
+ }
+
+ auto type_remove_marked_members()
+ -> void
+ {
+ assert (is_type() && initializer && initializer->is_compound());
+ auto compound_stmt = initializer->get_if<compound_statement_node>();
+ assert (compound_stmt);
+
+ // Note: This loop is a careful use of the brittle STL "erase" idiom. Do not change this
+ // loop without carefully ensuring it remains safe against iterator invalidation.
+ // (Especially don't change this to a for loop with a "++i" iteration-expression.)
+ auto i = compound_stmt->statements.begin();
+ while (i != compound_stmt->statements.end())
+ {
+ if ((*i)->marked_for_removal) {
+ i = compound_stmt->statements.erase(i); // these two branches ...
+ }
+ else {
+ ++i; // ... must stay together
+ }
+ }
+ }
+
+ auto type_remove_all_members()
+ -> void
+ {
+ assert (is_type() && initializer && initializer->is_compound());
+ auto body = initializer->get_if<compound_statement_node>();
+ assert (body);
+
+ // Drop all statements in the body, which should self-deregister all our 'captures'
+ // - (only) statements in the body should have been able to refer to 'captures'
+ body->statements.clear();
+ assert(captures.members.empty());
+ }
+
+ auto type_disable_member_function_generation()
+ -> void
+ {
+ member_function_generation = false;
+ }
+
+ auto object_type() const
+ -> std::string
+ {
+ if (!is_object()) {
+ return "(*ERROR*) not an object";
+ }
+ // Else
+ return std::get<an_object>(type)->to_string();
+ }
+
+ auto object_initializer() const
+ -> std::string
+ {
+ if (!is_object()) {
+ return "(*ERROR*) not an object";
+ }
+ else if (initializer) {
+ return initializer->to_string();
+ }
+ // Else
+ return "";
+ }
+
+ auto get_parent() const
+ -> declaration_node*
+ {
+ return parent_declaration;
+ }
+
+ auto is_public() const
+ -> bool
+ {
+ return access == accessibility::public_;
+ }
+
+ auto is_protected() const
+ -> bool
+ {
+ return access == accessibility::protected_;
+ }
+
+ auto is_private() const
+ -> bool
+ {
+ return access == accessibility::private_;
+ }
+
+ auto is_default_access() const
+ -> bool
+ {
+ return access == accessibility::default_;
+ }
+
+private:
+ auto set_access(accessibility a)
+ -> bool
+ {
+ if (is_default_access()) {
+ access = a;
+ }
+ return access == a;
+ }
+
+public:
+ auto make_public()
+ -> bool
+ {
+ return set_access( accessibility::public_ );
+ }
+
+ auto make_protected()
+ -> bool
+ {
+ return set_access( accessibility::protected_ );
+ }
+
+ auto make_private()
+ -> bool
+ {
+ return set_access( accessibility::private_ );
+ }
+
+ auto has_name() const
+ -> bool
+ {
+ return
+ identifier
+ && identifier->identifier
+ ;
+ }
+
+ auto name() const
+ -> token const*
+ {
+ if (!identifier) {
+ return nullptr;
+ }
+ // Else
+ return identifier->identifier;
+ }
+
+ auto has_name(std::string_view s) const
+ -> bool
+ {
+ return
+ has_name()
+ && *name() == s
+ ;
+ }
+
+ auto has_initializer() const
+ -> bool
+ {
+ return initializer != nullptr;
+ }
+
+ auto parameter_count() const
+ -> int
+ {
+ if (!is_function()) {
+ return -1;
+ }
+ return std::get<a_function>(type)->parameter_count();
+ }
+
+ auto index_of_parameter_named(std::string_view s) const
+ -> int
+ {
+ if (!is_function()) {
+ return -1;
+ }
+ return std::get<a_function>(type)->index_of_parameter_named(s);
+ }
+
+ auto has_parameter_named(std::string_view s) const
+ -> bool
+ {
+ if (!is_function()) {
+ return false;
+ }
+ return std::get<a_function>(type)->has_parameter_named(s);
+ }
+
+ auto has_in_parameter_named(std::string_view s) const
+ -> bool
+ {
+ if (!is_function()) {
+ return false;
+ }
+ return std::get<a_function>(type)->has_in_parameter_named(s);
+ }
+
+ auto has_out_parameter_named(std::string_view s) const
+ -> bool
+ {
+ if (!is_function()) {
+ return false;
+ }
+ return std::get<a_function>(type)->has_out_parameter_named(s);
+ }
+
+ auto has_move_parameter_named(std::string_view s) const
+ -> bool
+ {
+ if (!is_function()) {
+ return false;
+ }
+ return std::get<a_function>(type)->has_move_parameter_named(s);
+ }
+
+ auto nth_parameter_type_name(int n) const
+ -> std::string
+ {
+ if (!is_function()) {
+ return "";
+ }
+ return std::get<a_function>(type)->nth_parameter_type_name(n);
+ }
+
+ auto is_global () const -> bool
+ { return !parent_declaration; }
+
+ auto is_function () const -> bool
+ { return type.index() == a_function; }
+ auto is_object () const -> bool
+ { return type.index() == an_object; }
+ auto is_base_object() const -> bool
+ { return is_object() && has_name("this"); }
+ auto is_member_object() const -> bool
+ { return is_object() && !has_name("this"); }
+ auto is_concept () const -> bool
+ { return type.index() == an_object && get<an_object>(type)->is_concept(); }
+ auto is_type () const -> bool
+ { return type.index() == a_type; }
+ auto is_namespace() const -> bool
+ { return type.index() == a_namespace; }
+ auto is_alias() const -> bool
+ { return type.index() == an_alias; }
+
+ auto is_type_alias () const -> bool
+ { return is_alias() && std::get<an_alias>(type)->is_type_alias(); }
+ auto is_namespace_alias() const -> bool
+ { return is_alias() && std::get<an_alias>(type)->is_namespace_alias(); }
+ auto is_object_alias () const -> bool
+ { return is_alias() && std::get<an_alias>(type)->is_object_alias(); }
+
+ auto is_function_expression () const -> bool
+ { return is_function() && !identifier; }
+
+ auto is_polymorphic() const // has base types or virtual functions
+ -> bool
+ {
+ for (auto& decl : get_type_scope_declarations()) {
+ if (
+ decl->has_name("this")
+ || decl->is_virtual_function()
+ )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Do we know that this cannot be a copy constructible type?
+ auto cannot_be_a_copy_constructible_type() const
+ -> bool
+ {
+ // If we're not a type, we're not a copyable type
+ if (!is_type()) {
+ return true;
+ }
+
+ // Else if we're letting Cpp1 generate SMFs, we're likely copyable
+ if (!member_function_generation) {
+ return false;
+ }
+
+ // Else if we have a copy constructor, we're copyable
+ for (auto& decl : get_type_scope_declarations())
+ if (decl->is_constructor_with_that())
+ {
+ return false;
+ }
+
+ // Else there can't be a copy constructor
+ return true;
+ }
+
+ auto parent_is_function () const -> bool
+ { return parent_declaration && parent_declaration->type.index() == a_function; }
+ auto parent_is_object () const -> bool
+ { return parent_declaration && parent_declaration->type.index() == an_object; }
+ auto parent_is_type () const -> bool
+ { return parent_declaration && parent_declaration->type.index() == a_type; }
+ auto parent_is_namespace () const -> bool
+ { return !parent_declaration || parent_declaration->type.index() == a_namespace; }
+ auto parent_is_alias () const -> bool
+ { return parent_declaration && parent_declaration->type.index() == an_alias; }
+
+ auto parent_is_type_alias () const -> bool
+ { return parent_declaration && parent_declaration->is_alias() && std::get<an_alias>(parent_declaration->type)->is_type_alias(); }
+ auto parent_is_namespace_alias() const -> bool
+ { return parent_declaration && parent_declaration->is_alias() && std::get<an_alias>(parent_declaration->type)->is_namespace_alias(); }
+ auto parent_is_object_alias () const -> bool
+ { return parent_declaration && parent_declaration->is_alias() && std::get<an_alias>(parent_declaration->type)->is_object_alias(); }
+
+ auto is_inside_global_unnamed_function() const -> bool {
+ auto parent = parent_declaration;
+ // Get outside all nested function expressions
+ while (parent && parent->is_function() && !parent->has_name()) {
+ parent = parent->parent_declaration;
+ }
+ return !parent;
+ }
+
+ auto parent_is_polymorphic() const -> bool
+ { return parent_declaration && parent_declaration->is_polymorphic(); }
+
+ enum which {
+ functions = 1,
+ objects = 2,
+ types = 4,
+ aliases = 8,
+ all = functions|objects|types|aliases
+ };
+
+private:
+ // This helper is a const function that delivers pointers
+ // to non-const... because this is the best way I can
+ // think of right now to write the following two get_
+ // functions (without duplicating their bodies, and
+ // without resorting to const_casts)
+ auto gather_type_scope_declarations(which w) const
+ -> std::vector<declaration_node*>
+ {
+ if (
+ !is_type()
+ || !initializer
+ || !initializer->is_compound()
+ )
+ {
+ return {};
+ }
+
+ auto compound_stmt = initializer->get_if<compound_statement_node>();
+ assert (compound_stmt);
+
+ auto ret = std::vector<declaration_node*>{};
+ for (auto& o : compound_stmt->statements)
+ {
+ auto decl = o->get_if<declaration_node>();
+ if (decl)
+ {
+ assert(
+ !decl->is_namespace()
+ && "ICE: a type shouldn't be able to contain a namespace"
+ );
+ if (
+ (w & functions && decl->is_function())
+ || (w & objects && decl->is_object() )
+ || (w & types && decl->is_type() )
+ || (w & aliases && decl->is_alias() )
+ )
+ {
+ ret.push_back(decl);
+ }
+ }
+ }
+
+ return ret;
+ }
+
+public:
+ auto get_type_scope_declarations(which w = all)
+ -> std::vector<declaration_node*>
+ {
+ // Only want to return the gather_ results as
+ // non-const* in a non-const function
+ return gather_type_scope_declarations(w);
+ }
+
+ auto get_type_scope_declarations(which w = all) const
+ -> std::vector<declaration_node const*>
+ {
+ // Convert the gather_ results to const*
+ auto tmp = gather_type_scope_declarations(w);
+ return std::vector<declaration_node const*>(tmp.begin(), tmp.end());
+ }
+
+
+ auto add_type_member( std::unique_ptr<statement_node>&& statement )
+ -> bool
+ {
+ if (
+ !is_type()
+ || !initializer
+ || !initializer->is_compound()
+ || !statement->is_declaration()
+ )
+ {
+ return false;
+ }
+
+ // Tell this declaration statement that we are its new parent
+ // and check to ensure that it doesn't already have a parent
+ // (that shouldn't happen because we should only get here for a
+ // generated statement that hasn't been added elsewhere yet)
+ auto decl = statement->get_if<declaration_node>();
+ assert(
+ decl
+ && !decl->parent_declaration
+ );
+ decl->parent_declaration = this;
+
+ // And actually adopt it into our list of statements
+ auto compound_stmt = initializer->get_if<compound_statement_node>();
+ assert (compound_stmt);
+ compound_stmt->statements.push_back(std::move(statement));
+ return true;
+ }
+
+
+ auto add_function_initializer( std::unique_ptr<statement_node>&& statement )
+ -> bool
+ {
+ if (
+ !is_function()
+ || initializer
+ )
+ {
+ return false;
+ }
+
+ // Adopt it as our initializer statement
+ initializer = std::move( statement );
+ return true;
+ }
+
+
+ auto get_decl_if_type_scope_object_name_before_a_base_type( std::string_view s ) const
+ -> declaration_node const*
+ {
+ declaration_node const* ret = {};
+
+ // If it's 'this' then it can't be an object name
+ if (s == "this") {
+ return {};
+ }
+
+ // Navigate to the nearest enclosing type
+ auto decl = this;
+ while (
+ !decl->is_type()
+ && decl->parent_declaration
+ )
+ {
+ decl = decl->parent_declaration;
+ }
+
+ if (!decl->is_type()) {
+ return {};
+ }
+
+ // Look for a name match and if so remember the type,
+ // and look for a base type after that match
+ auto objects = decl->get_type_scope_declarations();
+ auto found_name = false;
+ auto found_later_base_type = false;
+
+ for (auto& o : objects) {
+ if (o->is_alias()) {
+ continue;
+ }
+ if (o->has_name(s)) {
+ found_name = true;
+ ret = o;
+ }
+ if (o->has_name("this")) {
+ if (found_name) {
+ found_later_base_type = true;
+ break;
+ }
+ }
+ }
+
+ // If we didn't find a later base type, discard any name match
+ if (!found_later_base_type) {
+ ret = {};
+ }
+
+ return ret;
+ }
+
+
+ auto get_initializer_statements() const
+ -> std::vector<statement_node*>
+ {
+ if (!initializer) {
+ return {};
+ }
+
+ auto ret = std::vector<statement_node*>{};
+ // For non-compound initializers, we want just that statement
+ if (!initializer->is_compound())
+ {
+ ret.push_back(initializer.get());
+ }
+
+ // Else for compound initializers, we want the compound_statement's statements
+ else
+ {
+ auto compound_stmt = initializer->get_if<compound_statement_node>();
+ assert (compound_stmt);
+ for (auto& o : compound_stmt->statements) {
+ ret.push_back(o.get());
+ }
+ }
+
+ return ret;
+ }
+
+ auto is_function_with_this() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_function_with_this();
+ }
+ // else
+ return false;
+ }
+
+ auto is_virtual_function() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_virtual_function();
+ }
+ // else
+ return false;
+ }
+
+ auto is_type_final() const
+ -> bool
+ {
+ if (auto t = std::get_if<a_type>(&type)) {
+ return (*t)->is_final();
+ }
+ // else
+ return false;
+ }
+
+ auto make_type_final()
+ -> bool
+ {
+ if (auto t = std::get_if<a_type>(&type)) {
+ (*t)->make_final();
+ return true;
+ }
+ // else
+ return false;
+ }
+
+ auto make_function_virtual()
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->make_function_virtual();
+ }
+ // else
+ return false;
+ }
+
+ auto is_defaultable_function() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_defaultable();
+ }
+ // else
+ return false;
+ }
+
+ auto is_constructor() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_constructor();
+ }
+ // else
+ return false;
+ }
+
+ auto is_default_constructor() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_default_constructor();
+ }
+ // else
+ return false;
+ }
+
+ auto is_move() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_move();
+ }
+ // else
+ return false;
+ }
+
+ auto is_swap() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_swap();
+ }
+ // else
+ return false;
+ }
+
+ auto is_constructor_with_that() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_constructor_with_that();
+ }
+ // else
+ return false;
+ }
+
+ auto is_constructor_with_in_that() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_constructor_with_in_that();
+ }
+ // else
+ return false;
+ }
+
+ auto is_constructor_with_move_that() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_constructor_with_move_that();
+ }
+ // else
+ return false;
+ }
+
+ auto is_comparison() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_comparison();
+ }
+ // else
+ return false;
+ }
+
+ auto is_increment_or_decrement() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_increment_or_decrement();
+ }
+ // else
+ return false;
+ }
+
+ auto is_compound_assignment() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_compound_assignment();
+ }
+ // else
+ return false;
+ }
+
+ auto is_assignment() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_assignment();
+ }
+ // else
+ return false;
+ }
+
+ auto is_assignment_with_that() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_assignment_with_that();
+ }
+ // else
+ return false;
+ }
+
+ auto is_assignment_with_in_that() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_assignment_with_in_that();
+ }
+ // else
+ return false;
+ }
+
+ auto is_assignment_with_move_that() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_assignment_with_move_that();
+ }
+ // else
+ return false;
+ }
+
+ struct declared_value_set_funcs {
+ declaration_node const* out_this_in_that = {};
+ declaration_node const* out_this_move_that = {};
+ declaration_node const* inout_this_in_that = {};
+ declaration_node const* inout_this_move_that = {};
+ std::vector<std::string> assignments_from = {};
+ };
+
+ auto find_declared_value_set_functions() const
+ -> declared_value_set_funcs
+ {
+ if (!initializer) {
+ return {};
+ }
+
+ auto compound_stmt = initializer->get_if<compound_statement_node>();
+ assert (compound_stmt);
+
+ auto ret = declared_value_set_funcs{};
+ for (auto& o : compound_stmt->statements)
+ {
+ auto decl = o->get_if<declaration_node>();
+ if (decl)
+ {
+ if (decl->is_constructor_with_in_that()) {
+ ret.out_this_in_that = decl;
+ }
+ if (decl->is_constructor_with_move_that()) {
+ ret.out_this_move_that = decl;
+ }
+ if (decl->is_assignment_with_in_that()) {
+ ret.inout_this_in_that = decl;
+ }
+ if (decl->is_assignment_with_move_that()) {
+ ret.inout_this_move_that = decl;
+ }
+ if (decl->is_assignment() && !decl->is_assignment_with_that()) {
+ ret.assignments_from.emplace_back( decl->nth_parameter_type_name(2) );
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ auto find_parent_declared_value_set_functions() const
+ -> declared_value_set_funcs
+ {
+ if (parent_is_type()) {
+ return parent_declaration->find_declared_value_set_functions();
+ }
+ // else
+ return {};
+ }
+
+
+ auto is_destructor() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->is_destructor();
+ }
+ // else
+ return false;
+ }
+
+ auto has_declared_return_type() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->has_declared_return_type();
+ }
+ // else
+ return false;
+ }
+
+ auto has_deduced_return_type() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->has_deduced_return_type();
+ }
+ // else
+ return false;
+ }
+
+ auto get_function_parameters()
+ -> std::vector<parameter_declaration_node const*>
+ {
+ if (!is_function()) {
+ return {};
+ }
+ // else
+ auto ret = std::vector<parameter_declaration_node const*>{};
+ for (auto& param : std::get<a_function>(type)->parameters->parameters) {
+ ret.push_back( param.get() );
+ }
+ return ret;
+ }
+
+ auto unnamed_return_type_to_string() const
+ -> std::string
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->unnamed_return_type_to_string();
+ }
+ // else
+ return {};
+ }
+
+ auto has_bool_return_type() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->has_bool_return_type();
+ }
+ // else
+ return false;
+ }
+
+ auto has_non_void_return_type() const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->has_non_void_return_type();
+ }
+ // else
+ return false;
+ }
+
+ auto has_parameter_with_name_and_pass(
+ std::string_view s,
+ passing_style pass
+ ) const
+ -> bool
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->has_parameter_with_name_and_pass(s, pass);
+ }
+ // else
+ return false;
+ }
+
+ auto first_parameter_name() const
+ -> std::string
+ {
+ if (auto func = std::get_if<a_function>(&type)) {
+ return (*func)->first_parameter_name();
+ }
+ // else
+ return "";
+ }
+
+ auto is_binary_comparison_function() const
+ -> bool
+ {
+ return
+ is_function()
+ && (
+ has_name("operator==")
+ || has_name("operator!=")
+ || has_name("operator<")
+ || has_name("operator<=")
+ || has_name("operator>")
+ || has_name("operator>=")
+ );
+ }
+
+ auto is_const() const
+ -> bool
+ {
+ return
+ type.index() == an_object
+ && !std::get<an_object>(type)->pc_qualifiers.empty()
+ && *std::get<an_object>(type)->pc_qualifiers.front() == "const"
+ ;
+ }
+
+ auto has_wildcard_type() const
+ -> bool
+ {
+ return
+ type.index() == an_object
+ && std::get<an_object>(type)->is_wildcard()
+ ;
+ }
+
+ auto get_object_type() const
+ -> type_id_node const*
+ {
+ if (type.index() == an_object) {
+ return std::get<an_object>(type).get();
+ }
+ // Else
+ return {};
+ }
+
+ // Internals
+ //
+ auto position() const
+ -> source_position
+ {
+ if (identifier) {
+ return identifier->position();
+ }
+ return pos;
+ }
+
+ auto visit(auto& v, int depth)
+ -> void
+ {
+ v.start(*this, depth);
+
+ v.start(declaration_identifier_tag{}, depth);
+ if (identifier) {
+ identifier->visit(v, depth+1);
+ }
+ v.end(declaration_identifier_tag{}, depth);
+
+ try_visit<a_function >(type, v, depth+1);
+ try_visit<an_object >(type, v, depth+1);
+ try_visit<a_type >(type, v, depth+1);
+ try_visit<a_namespace>(type, v, depth+1);
+ try_visit<an_alias >(type, v, depth+1);
+
+ for (auto& m : metafunctions) {
+ assert(m);
+ m->visit(v, depth+1);
+ }
+
+ if (initializer) {
+ initializer->visit(v, depth+1);
+ }
+
+ v.end(*this, depth);
+ }
+};
+
+
+compound_statement_node::compound_statement_node(source_position o)
+ : open_brace{o}
+{ }
+
+
+statement_node::statement_node(compound_statement_node* compound_parent_)
+ : compound_parent{ compound_parent_ }
+{ }
+
+
+function_type_node::function_type_node(declaration_node* decl)
+ : my_decl{decl}
+{ }
+
+
+auto parameter_declaration_node::has_name() const
+ -> bool
+{
+ return declaration->has_name();
+}
+
+
+auto parameter_declaration_node::name() const
+ -> token const*
+{
+ return declaration->name();
+}
+
+
+auto parameter_declaration_node::has_name(std::string_view s) const
+ -> bool
+{
+ return declaration->has_name(s);
+}
+
+
+auto function_type_node::first_parameter_name() const
+ -> std::string
+{
+ if (std::ssize(parameters->parameters) > 0)
+ {
+ assert (parameters->parameters[0]->declaration->name());
+ return parameters->parameters[0]->declaration->name()->to_string();
+ }
+ // Else
+ return "";
+}
+
+auto function_type_node::nth_parameter_type_name(int n) const
+ -> std::string
+{
+ if (std::ssize(parameters->parameters) >= n)
+ {
+ return parameters->parameters[n-1]->declaration->get_object_type()->to_string();
+ }
+ // Else
+ return "";
+}
+
+
+auto function_type_node::has_postconditions() const
+ -> bool
+{
+ return
+ std::find_if(
+ contracts.begin(),
+ contracts.end(),
+ [](auto const& e){ return *e->kind == "post"; }
+ ) != contracts.end();
+}
+
+auto function_type_node::is_function_with_this() const
+ -> bool
+{
+ if (
+ (*parameters).ssize() > 0
+ && (*parameters)[0]->has_name("this")
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_virtual_function() const
+ -> bool
+{
+ if (
+ (*parameters).ssize() > 0
+ && (*parameters)[0]->has_name("this")
+ && (*parameters)[0]->is_virtual()
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::make_function_virtual()
+ -> bool
+{
+ if (is_function_with_this()) {
+ (*parameters)[0]->make_virtual();
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_defaultable() const
+ -> bool
+{
+ if (
+ my_decl->has_name("operator==")
+ || my_decl->has_name("operator<=>")
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_constructor() const
+ -> bool
+{
+ if (
+ (*parameters).ssize() > 0
+ && (*parameters)[0]->has_name("this")
+ && (*parameters)[0]->direction() == passing_style::out
+ )
+ {
+ assert(my_decl->has_name("operator="));
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_default_constructor() const
+ -> bool
+{
+ if (
+ is_constructor()
+ && (*parameters).ssize() == 1
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_move() const
+ -> bool
+{
+ if (
+ (is_constructor() || is_assignment())
+ && (*parameters).ssize() == 2
+ && (*parameters)[1]->has_name("that")
+ && (*parameters)[1]->direction() == passing_style::move
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_swap() const
+ -> bool
+{
+ assert (my_decl);
+ if (
+ my_decl->has_name("swap")
+ && (*parameters).ssize() == 2
+ && (*parameters)[1]->has_name("that")
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_constructor_with_that() const
+ -> bool
+{
+ if (
+ is_constructor()
+ && (*parameters).ssize() == 2
+ && (*parameters)[1]->has_name("that")
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_assignment_with_that() const
+ -> bool
+{
+ if (
+ is_assignment()
+ && (*parameters).ssize() == 2
+ && (*parameters)[1]->has_name("that")
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_constructor_with_in_that() const
+ -> bool
+{
+ if (
+ is_constructor()
+ && (*parameters).ssize() == 2
+ && (*parameters)[1]->has_name("that")
+ && (*parameters)[1]->direction() == passing_style::in
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_constructor_with_move_that() const
+ -> bool
+{
+ if (
+ is_constructor()
+ && (*parameters).ssize() == 2
+ && (*parameters)[1]->has_name("that")
+ && (*parameters)[1]->direction() == passing_style::move
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_comparison() const
+ -> bool
+{
+ if (
+ (
+ my_decl->has_name("operator==")
+ || my_decl->has_name("operator!=")
+ || my_decl->has_name("operator<")
+ || my_decl->has_name("operator<=")
+ || my_decl->has_name("operator>")
+ || my_decl->has_name("operator>=")
+ || my_decl->has_name("operator<=>")
+ )
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_increment_or_decrement() const
+ -> bool
+{
+ if (
+ my_decl->has_name("operator++")
+ || my_decl->has_name("operator--")
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_compound_assignment() const
+ -> bool
+{
+ if (
+ (
+ my_decl->has_name("operator+=")
+ || my_decl->has_name("operator-=")
+ || my_decl->has_name("operator*=")
+ || my_decl->has_name("operator/=")
+ || my_decl->has_name("operator%=")
+ || my_decl->has_name("operator&=")
+ || my_decl->has_name("operator|=")
+ || my_decl->has_name("operator^=")
+ || my_decl->has_name("operator<<=")
+ || my_decl->has_name("operator>>=")
+ )
+ && (*parameters).ssize() > 1
+ && (*parameters)[0]->has_name("this")
+ && (*parameters)[0]->direction() == passing_style::inout
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_assignment() const
+ -> bool
+{
+ if (
+ my_decl->has_name("operator=")
+ && (*parameters).ssize() > 1
+ && (*parameters)[0]->has_name("this")
+ && (*parameters)[0]->direction() == passing_style::inout
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_assignment_with_in_that() const
+ -> bool
+{
+ if (
+ is_assignment()
+ && (*parameters).ssize() == 2
+ && (*parameters)[1]->has_name("that")
+ && (*parameters)[1]->direction() == passing_style::in
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_assignment_with_move_that() const
+ -> bool
+{
+ if (
+ is_assignment()
+ && (*parameters).ssize() == 2
+ && (*parameters)[1]->has_name("that")
+ && (*parameters)[1]->direction() == passing_style::move
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto function_type_node::is_destructor() const
+ -> bool
+{
+ if (
+ my_decl->has_name("operator=")
+ && (*parameters).ssize() == 1
+ && (*parameters)[0]->has_name("this")
+ && (*parameters)[0]->direction() == passing_style::move
+ )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+auto primary_expression_node::template_arguments() const
+ -> std::vector<template_argument> const&
+{
+ if (expr.index() == id_expression) {
+ return std::get<id_expression>(expr)->template_arguments();
+ }
+ // else
+ return no_template_args;
+}
+
+
+auto primary_expression_node::get_token() const
+ -> token const*
+{
+ if (expr.index() == identifier) {
+ return std::get<identifier>(expr);
+ }
+ else if (expr.index() == id_expression) {
+ return std::get<id_expression>(expr)->get_token();
+ }
+ else if (expr.index() == literal) {
+ return std::get<literal>(expr)->get_token();
+ }
+ // else (because we're deliberately ignoring the other
+ // options which are more than a single token)
+ return {};
+}
+
+
+auto primary_expression_node::to_string() const
+ -> std::string
+{
+ switch (expr.index())
+ {
+ break;case empty:
+ return {};
+
+ break;case identifier: {
+ auto const& s = std::get<identifier>(expr);
+ assert (s);
+ return s->to_string();
+ }
+
+ break;case id_expression: {
+ auto const& s = std::get<id_expression>(expr);
+ assert (s);
+ return s->to_string();
+ }
+
+ break;case literal: {
+ auto const& i = std::get<literal>(expr);
+ assert (i);
+ return i->to_string();
+ }
+
+ break;default:
+ return "(*ERROR*) temporary alpha limitation: type metafunctions cannot stringize expressions that involve nested expression-lists, declarations, or inspect expressions";
+ }
+}
+
+
+auto primary_expression_node::position() const
+ -> source_position
+{
+ switch (expr.index())
+ {
+ break;case empty:
+ return { 0, 0 };
+
+ break;case identifier: {
+ auto const& s = std::get<identifier>(expr);
+ assert (s);
+ return s->position();
+ }
+
+ break;case expression_list: {
+ auto const& s = std::get<expression_list>(expr);
+ assert (s);
+ return s->position();
+ }
+
+ break;case id_expression: {
+ auto const& s = std::get<id_expression>(expr);
+ assert (s);
+ return s->position();
+ }
+
+ break;case declaration: {
+ auto const& s = std::get<declaration>(expr);
+ assert (s);
+ return s->position();
+ }
+
+ break;case inspect: {
+ auto const& i = std::get<inspect>(expr);
+ assert (i);
+ return i->position();
+ }
+
+ break;case literal: {
+ auto const& i = std::get<literal>(expr);
+ assert (i);
+ return i->position();
+ }
+
+ break;default:
+ assert (!"illegal primary_expression_node state");
+ return { 0, 0 };
+ }
+}
+
+
+auto primary_expression_node::visit(auto& v, int depth)
+ -> void
+{
+ v.start(*this, depth);
+ try_visit<identifier >(expr, v, depth);
+ try_visit<expression_list>(expr, v, depth);
+ try_visit<id_expression >(expr, v, depth);
+ try_visit<declaration >(expr, v, depth);
+ try_visit<inspect >(expr, v, depth);
+ try_visit<literal >(expr, v, depth);
+ v.end(*this, depth);
+}
+
+
+struct next_expression_tag { };
+struct loop_body_tag { token const* identifier; };
+
+auto iteration_statement_node::visit(auto& v, int depth)
+ -> void
+{
+ v.start(*this, depth);
+ if (label) {
+ label->visit(v, depth+1);
+ }
+ if (identifier) {
+ identifier->visit(v, depth+1);
+ }
+ if (statements) {
+ statements->visit(v, depth+1);
+ }
+ if (next_expression) {
+ v.start(next_expression_tag{}, depth);
+ next_expression->visit(v, depth+1);
+ v.end(next_expression_tag{}, depth);
+ }
+ if (condition) {
+ assert(!range && !body);
+ condition->visit(v, depth+1);
+ }
+ else {
+ assert(range && parameter && body);
+ range->visit(v, depth+1);
+ v.start(loop_body_tag{identifier}, depth);
+ parameter->visit(v, depth+1);
+ body->visit(v, depth+1);
+ }
+ v.end(*this, depth);
+}
+
+
+auto statement_node::position() const
+ -> source_position
+{
+ switch (statement.index())
+ {
+ break;case expression: {
+ auto const& s = std::get<expression>(statement);
+ assert (s);
+ return s->position();
+ }
+
+ break;case compound: {
+ auto const& s = std::get<compound>(statement);
+ assert (s);
+ return s->position();
+ }
+
+ break;case selection: {
+ auto const& s = std::get<selection>(statement);
+ assert (s);
+ return s->position();
+ }
+
+ break;case declaration: {
+ auto const& s = std::get<declaration>(statement);
+ assert (s);
+ return s->position();
+ }
+
+ break;case return_: {
+ auto const& s = std::get<return_>(statement);
+ assert (s);
+ return s->position();
+ }
+
+ break;case iteration: {
+ auto const& s = std::get<iteration>(statement);
+ assert (s);
+ return s->position();
+ }
+
+ break;case using_: {
+ auto const& s = std::get<using_>(statement);
+ assert (s);
+ return s->position();
+ }
+
+ break;case contract: {
+ auto const& s = std::get<contract>(statement);
+ assert (s);
+ return s->position();
+ }
+
+ break;case inspect: {
+ auto const& s = std::get<inspect>(statement);
+ assert (s);
+ return s->position();
+ }
+
+ break;case jump: {
+ auto const& s = std::get<jump>(statement);
+ assert (s);
+ return s->position();
+ }
+
+ break;default:
+ assert (!"illegal statement_node state");
+ return { 0, 0 };
+ }
+}
+
+
+auto parameter_declaration_node::position() const
+ -> source_position
+{
+ assert (declaration);
+ return pos;
+}
+
+
+auto parameter_declaration_node::visit(auto& v, int depth)
+ -> void
+{
+ v.start(*this, depth);
+ assert(declaration);
+ declaration->visit(v, depth + 1);
+ v.end(*this, depth);
+}
+
+
+struct translation_unit_node
+{
+ std::vector< std::unique_ptr<declaration_node> > declarations;
+
+ auto position() const -> source_position
+ {
+ if (std::ssize(declarations) > 0) {
+ return declarations.front()->position();
+ }
+ return {};
+ }
+
+ auto visit(auto& v, int depth) -> void
+ {
+ v.start(*this, depth);
+ for (auto const& x : declarations) {
+ assert(x);
+ x->visit(v, depth + 1);
+ }
+ v.end(*this, depth);
+ }
+};
+
+
+//-----------------------------------------------------------------------
+//
+// pretty_print_visualize: pretty-prints Cpp2 ASTs
+//
+//-----------------------------------------------------------------------
+//
+auto pretty_print_visualize(token const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(primary_expression_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(literal_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(prefix_expression_node const& n, int indent)
+ -> std::string;
+template<
+ String Name,
+ typename Term
+>
+auto pretty_print_visualize(binary_expression_node<Name,Term> const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(expression_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(expression_list_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(expression_statement_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(postfix_expression_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(unqualified_id_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(qualified_id_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(type_id_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(is_as_expression_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(id_expression_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(compound_statement_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(selection_statement_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(iteration_statement_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(return_statement_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(alternative_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(inspect_expression_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(contract_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(jump_statement_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(using_statement_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(statement_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(parameter_declaration_node const& n, int indent, bool is_template_param = false)
+ -> std::string;
+auto pretty_print_visualize(parameter_declaration_list_node const& n, int indent, bool is_template_param_list = false)
+ -> std::string;
+auto pretty_print_visualize(function_type_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(type_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(namespace_node const& n, int indent)
+ -> std::string;
+auto pretty_print_visualize(declaration_node const& n, int indent, bool include_metafunctions_list = false)
+ -> std::string;
+
+
+
+//-----------------------------------------------------------------------
+// pre: Get an indentation prefix
+//
+inline static int indent_spaces = 2;
+inline static std::string indent_str = std::string( 1024, ' ' ); // "1K should be enough for everyone"
+
+auto pre(int indent)
+ -> std::string_view
+{
+ assert (indent >= 0);
+ return {
+ indent_str.c_str(),
+ as<size_t>( std::min( indent*indent_spaces, _as<int>(std::ssize(indent_str))) )
+ };
+}
+
+
+//-----------------------------------------------------------------------
+// try_pretty_print_visualize
+//
+// Helper to emit whatever is in a variant where each
+// alternative is a smart pointer
+//
+template <int I>
+auto try_pretty_print_visualize(
+ auto& v,
+ auto&&... more
+)
+ -> std::string
+{
+ if (v.index() == I) {
+ auto const& alt = std::get<I>(v);
+ assert (alt);
+ return pretty_print_visualize (*alt, CPP2_FORWARD(more)...);
+ }
+ return "";
+}
+
+
+auto pretty_print_visualize(token const& t, int)
+ -> std::string
+{
+ return t.to_string();
+}
+
+
+auto pretty_print_visualize(primary_expression_node const& n, int indent)
+ -> std::string
+{
+ auto ret = std::string{};
+
+ ret += try_pretty_print_visualize<primary_expression_node::identifier >(n.expr, indent);
+ ret += try_pretty_print_visualize<primary_expression_node::expression_list>(n.expr, indent);
+ ret += try_pretty_print_visualize<primary_expression_node::id_expression >(n.expr, indent);
+ ret += try_pretty_print_visualize<primary_expression_node::declaration >(n.expr, indent);
+ ret += try_pretty_print_visualize<primary_expression_node::inspect >(n.expr, indent);
+ ret += try_pretty_print_visualize<primary_expression_node::literal >(n.expr, indent);
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(literal_node const& n, int)
+ -> std::string
+{
+ // TODO: This is an initial visualizer implementation, and still
+ // skips a few rarer things (such as raw string literals)
+
+ assert(n.literal);
+
+ auto ret = n.literal->to_string();
+
+ if (n.user_defined_suffix) {
+ ret += n.user_defined_suffix->as_string_view();
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(prefix_expression_node const& n, int indent)
+ -> std::string
+{
+ assert(n.expr);
+
+ auto ret = std::string{};
+
+ for (auto& op : n.ops) {
+ assert(op);
+ ret += op->as_string_view();
+ }
+
+ ret += pretty_print_visualize(*n.expr, indent);
+
+ return ret;
+}
+
+
+template<
+ String Name,
+ typename Term
+>
+auto pretty_print_visualize(binary_expression_node<Name,Term> const& n, int indent)
+ -> std::string
+{
+ assert(n.expr);
+
+ auto ret = pretty_print_visualize(*n.expr, indent);
+ for (auto& term : n.terms) {
+ assert(term.op && term.expr);
+ ret += " " + term.op->to_string()
+ + " " + pretty_print_visualize(*term.expr, indent);
+ }
+ return ret;
+}
+
+
+auto pretty_print_visualize(expression_node const& n, int indent)
+ -> std::string
+{
+ assert(n.expr);
+ return pretty_print_visualize(*n.expr, indent);
+}
+
+
+auto pretty_print_visualize(expression_list_node const& n, int indent)
+ -> std::string
+{
+ assert(n.open_paren && n.close_paren);
+
+ auto ret = n.open_paren->to_string();
+
+ for (auto i = 0; auto& expr : n.expressions) {
+ assert(expr.expr);
+ if (
+ expr.pass == passing_style::out
+ || expr.pass == passing_style::move
+ || expr.pass == passing_style::forward
+ )
+ {
+ ret += to_string_view(expr.pass) + std::string{" "};
+ }
+ ret += pretty_print_visualize(*expr.expr, indent);
+ if (++i < std::ssize(n.expressions)) {
+ ret += ", ";
+ }
+ }
+
+ ret += n.close_paren->as_string_view();
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(expression_statement_node const& n, int indent)
+ -> std::string
+{
+ assert(n.expr);
+
+ auto ret = pretty_print_visualize(*n.expr, indent);
+
+ if (n.has_semicolon && ret.back() != ';') {
+ ret += ";";
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(postfix_expression_node const& n, int indent)
+ -> std::string
+{
+ assert(n.expr);
+
+ auto ret = pretty_print_visualize(*n.expr, indent);
+
+ for (auto& op : n.ops)
+ {
+ assert(op.op);
+ if (op.expr_list) {
+ assert (op.op_close);
+ ret += pretty_print_visualize(*op.expr_list, indent);
+ }
+ else {
+ ret += op.op->as_string_view();
+ if (op.id_expr) {
+ ret += pretty_print_visualize(*op.id_expr, indent);
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(unqualified_id_node const& n, int indent)
+ -> std::string
+{
+ assert(n.identifier);
+
+ auto ret = n.identifier->to_string();
+
+ if (n.open_angle != source_position{})
+ {
+ ret += "<";
+ for (bool first = true; auto& arg : n.template_args)
+ {
+ if (!first) {
+ ret += ", ";
+ }
+ first = false;
+ ret += try_pretty_print_visualize<template_argument::expression>(arg.arg, indent);
+ ret += try_pretty_print_visualize<template_argument::type_id >(arg.arg, indent);
+ }
+ ret += ">";
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(qualified_id_node const& n, int indent)
+ -> std::string
+{
+ auto ret = std::string{};
+
+ for (auto& id : n.ids) {
+ if (id.scope_op) { ret += id.scope_op->as_string_view(); }
+ assert (id.id);
+ ret += pretty_print_visualize(*id.id, indent);
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(type_id_node const& n, int indent)
+ -> std::string
+{
+ auto ret = std::string{};
+
+ for (auto& qual : n.pc_qualifiers) {
+ assert(qual);
+ ret += qual->as_string_view();
+ ret += " ";
+ }
+
+ if (n.id.index() == type_id_node::empty) { ret += "_"; }
+ ret += try_pretty_print_visualize<type_id_node::qualified >(n.id, indent);
+ ret += try_pretty_print_visualize<type_id_node::unqualified>(n.id, indent);
+ ret += try_pretty_print_visualize<type_id_node::keyword >(n.id, indent);
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(is_as_expression_node const& n, int indent)
+ -> std::string
+{
+ assert (n.expr);
+
+ auto ret = pretty_print_visualize(*n.expr, indent);
+
+ for (auto& op : n.ops) {
+ if (op.op) { ret += " " + op.op->to_string() + " "; }
+ if (op.type) { ret += pretty_print_visualize(*op.type, indent); }
+ if (op.expr) { ret += pretty_print_visualize(*op.expr, indent); }
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(id_expression_node const& n, int indent)
+ -> std::string
+{
+ auto ret = std::string{};
+
+ ret += try_pretty_print_visualize<id_expression_node::qualified >(n.id, indent);
+ ret += try_pretty_print_visualize<id_expression_node::unqualified>(n.id, indent);
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(compound_statement_node const& n, int indent)
+ -> std::string
+{
+ auto ret = std::string{"\n"} + pre(indent) + "{";
+
+ for (auto& stmt : n.statements) {
+ assert (stmt);
+ ret += pretty_print_visualize(*stmt, indent+1);
+ }
+
+ ret += std::string{"\n"} + pre(indent) + "}";
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(selection_statement_node const& n, int indent)
+ -> std::string
+{
+ assert (n.identifier && n.expression && n.true_branch && n.false_branch);
+
+ auto ret = std::string{};
+
+ ret += std::string{"\n"} + pre(indent) + n.identifier->as_string_view() + " ";
+
+ if (n.is_constexpr) {
+ ret += "constexpr ";
+ }
+
+ ret += pretty_print_visualize(*n.expression, indent)
+ + pretty_print_visualize(*n.true_branch, indent);
+
+ if (n.has_source_false_branch) {
+ ret += std::string{"\n"} + pre(indent) + "else "
+ + pretty_print_visualize(*n.false_branch, indent);
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(iteration_statement_node const& n, int indent)
+ -> std::string
+{
+ // First compute the common parts
+
+ auto next_expr = std::string{};
+ if (n.next_expression) {
+ next_expr += std::string{"\n"} + pre(indent) + "next " + pretty_print_visualize(*n.next_expression, indent);
+ }
+
+ auto stmts = std::string{};
+ if (n.statements) {
+ stmts += pretty_print_visualize(*n.statements, indent+1);
+ }
+
+ // Then slot them in where appropriate
+
+ auto ret = std::string{};
+ assert (n.identifier);
+
+ ret += std::string{"\n"} + pre(indent);
+ if (n.label) {
+ ret += n.label->to_string()
+ + ": ";
+ }
+
+ if (*n.identifier == "while") {
+ assert (n.condition);
+ ret += "while "
+ + pretty_print_visualize(*n.condition, indent) + next_expr + stmts;
+ }
+ else if (*n.identifier == "do") {
+ assert (n.condition);
+ ret += "do "
+ + stmts
+ + next_expr
+ + "\n" + pre(indent) + "while "
+ + pretty_print_visualize(*n.condition, indent);
+ if (ret.back() != ';') {
+ ret += ";";
+ }
+ }
+ else {
+ assert (n.range && n.parameter && n.body);
+ ret += "for "
+ + pretty_print_visualize(*n.range, indent)
+ + next_expr
+ + "\n" + pre(indent) + "do (" + pretty_print_visualize(*n.parameter, indent + 1) + ")"
+ + pretty_print_visualize(*n.body, indent+1);
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(return_statement_node const& n, int indent)
+ -> std::string
+{
+ auto ret = std::string{"\n"} + pre(indent) + "return";
+
+ if (n.expression) {
+ ret += " " + pretty_print_visualize(*n.expression, indent);
+ }
+
+ if (ret.back() != ';') {
+ ret += ";";
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(alternative_node const& n, int indent)
+ -> std::string
+{
+ auto ret = std::string{};
+ assert (n.is_as_keyword);
+ ret += std::string{"\n"} + pre(indent);
+ if (n.name) {
+ ret += pretty_print_visualize(*n.name, indent) + ": ";
+ }
+ ret += n.is_as_keyword->as_string_view();
+ if (n.type_id) {
+ ret += " " + pretty_print_visualize(*n.type_id, indent);
+ }
+ if (n.value) {
+ ret += " " + pretty_print_visualize(*n.value, indent);
+ }
+ ret += " = " + pretty_print_visualize(*n.statement, indent+1);
+ return ret;
+}
+
+
+auto pretty_print_visualize(inspect_expression_node const& n, int indent)
+ -> std::string
+{
+ assert (n.expression);
+
+ auto ret = std::string{"inspect"};
+
+ if (n.is_constexpr) {
+ ret += " constexpr";
+ }
+
+ ret += " " + pretty_print_visualize(*n.expression, indent);
+
+ if (n.result_type) {
+ ret += " -> " + pretty_print_visualize(*n.result_type, indent);
+ }
+
+ ret += " {";
+
+ for (auto& alt : n.alternatives) {
+ assert(alt);
+ ret += pretty_print_visualize(*alt, indent+1);
+ }
+
+ ret += std::string{"\n"} + pre(indent) + "}";
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(contract_node const& n, int indent)
+ -> std::string
+{
+ assert (n.kind && n.condition);
+
+ auto ret = std::string{"\n"} + pre(indent) + n.kind->as_string_view();
+
+ if (n.group) {
+ ret += "<" + pretty_print_visualize(*n.group, indent);
+ for (auto const& flag : n.flags) {
+ ret += "," + pretty_print_visualize(*flag, indent);
+ }
+ ret += ">";
+ }
+
+ ret += "( " + pretty_print_visualize(*n.condition, indent);
+
+ if (n.message) {
+ ret += ", " + pretty_print_visualize(*n.message, indent);
+ }
+
+ ret += " )";
+
+ if (*n.kind == "assert" && ret.back() != ';') {
+ ret += ";";
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(jump_statement_node const& n, int indent)
+ -> std::string
+{
+ assert (n.keyword);
+
+ auto ret = std::string{"\n"} + pre(indent) + n.keyword->as_string_view();
+
+ if (n.label) {
+ ret += " " + n.label->to_string();
+ }
+
+ if (ret.back() != ';') {
+ ret += ";";
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(using_statement_node const& n, int indent)
+ -> std::string
+{
+ assert (n.keyword);
+
+ auto ret = std::string{"\n"} + pre(indent) + n.keyword->as_string_view() + " ";
+
+ if (n.for_namespace) {
+ ret += "namespace ";
+ }
+
+ ret += pretty_print_visualize(*n.id, indent);
+ if (ret.back() != ';') {
+ ret += ";";
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(statement_node const& n, int indent)
+ -> std::string
+{
+ auto ret = std::string{};
+
+ if (n.is_expression())
+ {
+ if (n.compound_parent) {
+ ret += std::string{"\n"} + pre(indent);
+ }
+ auto& expr = std::get<statement_node::expression>(n.statement);
+ assert (expr);
+ ret += pretty_print_visualize(*expr, indent);
+ }
+ else
+ {
+ if (n.parameters) {
+ ret += std::string{"\n"} + pre(indent) + pretty_print_visualize(*n.parameters, indent);
+ }
+
+ ret += try_pretty_print_visualize<statement_node::compound >(n.statement, indent);
+ ret += try_pretty_print_visualize<statement_node::selection >(n.statement, indent);
+ ret += try_pretty_print_visualize<statement_node::declaration>(n.statement, indent);
+ ret += try_pretty_print_visualize<statement_node::return_ >(n.statement, indent);
+ ret += try_pretty_print_visualize<statement_node::iteration >(n.statement, indent);
+ ret += try_pretty_print_visualize<statement_node::using_ >(n.statement, indent);
+ ret += try_pretty_print_visualize<statement_node::contract >(n.statement, indent);
+ ret += try_pretty_print_visualize<statement_node::inspect >(n.statement, indent);
+ ret += try_pretty_print_visualize<statement_node::jump >(n.statement, indent);
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(parameter_declaration_node const& n, int indent, bool is_template_param_list /* = false */ )
+ -> std::string
+{
+ assert (n.declaration);
+
+ auto ret = std::string{};
+
+ if (!is_template_param_list) {
+ switch (n.mod) {
+ break;case parameter_declaration_node::modifier::implicit : ret += "implicit ";
+ break;case parameter_declaration_node::modifier::virtual_ : ret += "virtual ";
+ break;case parameter_declaration_node::modifier::override_: ret += "override ";
+ break;case parameter_declaration_node::modifier::final_ : ret += "final ";
+ break;default: ; // none
+ }
+
+ ret += to_string_view(n.pass);
+ ret += " ";
+ }
+
+ ret += pretty_print_visualize(*n.declaration, indent);
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(parameter_declaration_list_node const& n, int indent, bool is_template_param_list /* = false */)
+ -> std::string
+{
+ assert(n.open_paren && n.close_paren);
+
+ auto ret = n.open_paren->to_string();
+
+ auto space = std::string{};
+ if (std::ssize(n.parameters) > 1) {
+ space += std::string{"\n"} + pre(indent+1);
+ }
+
+ for (auto i = 0; auto& param : n.parameters) {
+ ret += space + pretty_print_visualize(*param, indent+1, is_template_param_list);
+ if (++i < std::ssize(n.parameters)) {
+ ret += ", ";
+ }
+ }
+
+ if (std::ssize(n.parameters) > 1) {
+ ret += std::string{"\n"} + pre(indent);
+ }
+ ret += n.close_paren->to_string();
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(function_type_node const& n, int indent)
+ -> std::string
+{
+ assert (n.parameters);
+
+ auto ret = pretty_print_visualize(*n.parameters, indent);
+
+ if (n.throws) {
+ ret += " throws";
+ }
+
+ if (n.has_non_void_return_type()) {
+ ret += " -> ";
+ ret += try_pretty_print_visualize<function_type_node::list>(n.returns, indent+1);
+ if (n.returns.index() == function_type_node::id) {
+ auto& single = std::get<function_type_node::id>(n.returns);
+ ret += to_string_view(single.pass)
+ + std::string{" "} + pretty_print_visualize(*single.type, indent+1);
+ }
+ }
+
+ for (auto& contract: n.contracts) {
+ assert(contract);
+ ret += pretty_print_visualize(*contract, indent+1);
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(type_node const& n)
+ -> std::string
+{
+ assert (n.type);
+
+ auto ret = std::string{};
+
+ if (n.final) {
+ ret += "final ";
+ }
+
+ ret += "type";
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(namespace_node const&)
+ -> std::string
+{
+ return "namespace";
+}
+
+
+auto pretty_print_visualize(declaration_node const& n, int indent, bool include_metafunctions_list /* = false */ )
+ -> std::string
+{
+ indent_spaces = 4;
+
+ // First compute the common parts
+
+ auto metafunctions = std::string{};
+ if (include_metafunctions_list) {
+ for (auto& meta : n.metafunctions) {
+ metafunctions += " @" + pretty_print_visualize(*meta, indent);
+ }
+ }
+
+ auto template_params = std::string{};
+ if (n.template_parameters) {
+ template_params += " " + pretty_print_visualize(*n.template_parameters, indent + 1, true);
+ }
+
+ auto requires_clause = std::string{};
+ if (n.requires_clause_expression) {
+ requires_clause += " requires (" + pretty_print_visualize(*n.requires_clause_expression, indent) + ")";
+ }
+
+ auto initializer = std::string{};
+ if (n.initializer) {
+ auto adjusted_indent = indent;
+ if (!n.name()) {
+ ++adjusted_indent;
+ }
+ initializer = " =";
+ if (n.is_function() && n.is_constexpr) {
+ initializer += "=";
+ }
+ initializer += " " + pretty_print_visualize(*n.initializer, adjusted_indent);
+ if (initializer.ends_with(";;")) {
+ initializer.pop_back();
+ }
+ }
+ else if (!n.is_parameter) {
+ initializer = ";";
+ }
+
+ // Then slot them in where appropriate
+
+ auto ret = std::string{""};
+
+ // Add an extra newline for spacing, unless this declaration
+ // is within a function body or is the first member of a type
+ if (
+ !n.parent_is_function()
+ && !n.parent_is_object()
+ && !n.is_parameter
+ )
+ {
+ static declaration_node const* last_parent_type = {};
+ if (n.parent_is_type()) {
+ if (last_parent_type != n.get_parent()) {
+ last_parent_type = n.get_parent();
+ }
+ else {
+ ret += "\n";
+ }
+ }
+ else {
+ ret += "\n";
+ }
+ }
+ if (!n.is_parameter && n.name()) {
+ ret += std::string{"\n"} + pre(indent);
+ }
+
+ switch (n.access) {
+ break;case accessibility::public_ : ret += "public ";
+ break;case accessibility::protected_ : ret += "protected ";
+ break;case accessibility::private_ : ret += "private ";
+ break;default: ; // default accessibility
+ }
+
+ if (n.identifier) {
+ ret += pretty_print_visualize(*n.identifier, indent);
+ }
+
+ if (n.is_parameter && (n.has_name("this") || n.has_name("that"))) {
+ return ret;
+ }
+
+ if (n.is_variadic) {
+ ret += "...";
+ }
+
+ ret += ":";
+
+ if (n.is_function()) {
+ auto& func = std::get<declaration_node::a_function>(n.type);
+ assert(func);
+ ret += metafunctions
+ + template_params
+ + pretty_print_visualize(*func, indent)
+ + requires_clause
+ + initializer;
+ }
+ else if (n.is_object()) {
+ auto& type_id = std::get<declaration_node::an_object>(n.type);
+ assert(type_id);
+ ret += metafunctions
+ + template_params;
+ if (!n.has_wildcard_type()) {
+ ret += " " + pretty_print_visualize(*type_id, indent);
+ }
+ ret += requires_clause
+ + initializer;
+ }
+ else if (n.is_type()) {
+ auto& t = std::get<declaration_node::a_type>(n.type);
+ assert(t);
+ ret += metafunctions
+ + template_params
+ + " " + pretty_print_visualize(*t)
+ + initializer;
+ }
+ else if (n.is_namespace()) {
+ auto& t = std::get<declaration_node::a_type>(n.type);
+ assert(t);
+ ret += "namespace = "
+ + initializer;
+ }
+ else if (n.is_alias()) {
+ auto& a = std::get<declaration_node::an_alias>(n.type);
+ assert(a);
+
+ auto object_type_id = std::string{};
+ if (a->type_id) {
+ object_type_id += " " + pretty_print_visualize(*a->type_id, indent);
+ }
+
+ ret += template_params;
+ if (a->is_type_alias()) {
+ auto& t = std::get<alias_node::a_type>(a->initializer);
+ ret += " type"
+ + requires_clause
+ + " == "
+ + pretty_print_visualize(*t, indent);
+ if (ret.back() != ';') {
+ ret += ";";
+ }
+ }
+ else if (a->is_namespace_alias()) {
+ auto& id = std::get<alias_node::a_namespace>(a->initializer);
+ assert(id);
+ ret += " namespace == "
+ + pretty_print_visualize(*id, indent);
+ if (ret.back() != ';') {
+ ret += ";";
+ }
+ }
+ else if (a->is_object_alias()) {
+ auto& expr = std::get<alias_node::an_object>(a->initializer);
+ assert(expr);
+ ret += object_type_id
+ + requires_clause
+ + " == "
+ + pretty_print_visualize(*expr, indent);
+ if (ret.back() != ';') {
+ ret += ";";
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+auto pretty_print_visualize(translation_unit_node const& n)
+ -> std::string
+{
+ auto ret = std::string{};
+
+ for (auto& decl : n.declarations) {
+ assert(decl);
+ ret += pretty_print_visualize(*decl, 0);
+ }
+
+ return ret;
+}
+
+
+//-----------------------------------------------------------------------
+//
+// parser: parses a section of Cpp2 code
+//
+//-----------------------------------------------------------------------
+//
+class parser
+{
+ std::vector<error_entry>& errors;
+
+ std::unique_ptr<translation_unit_node> parse_tree = {};
+
+ // Keep a stack of current capture groups (contracts/decls still being parsed)
+ std::vector<capture_group*> current_capture_groups = {};
+
+ struct capture_groups_stack_guard
+ {
+ parser* pars;
+
+ capture_groups_stack_guard(parser* p, capture_group* cg)
+ : pars{ p }
+ {
+ assert(p);
+ assert(cg);
+ pars->current_capture_groups.push_back(cg);
+ }
+
+ ~capture_groups_stack_guard()
+ {
+ pars->current_capture_groups.pop_back();
+ }
+ };
+
+ // Keep a stack of currently active declarations (still being parsed)
+ std::vector<declaration_node*> current_declarations = { nullptr };
+
+ struct current_declarations_stack_guard
+ {
+ parser* pars;
+
+ current_declarations_stack_guard(parser* p, declaration_node* decl)
+ : pars{ p }
+ {
+ assert(p);
+ assert(decl);
+ pars->current_declarations.push_back(decl);
+ }
+
+ ~current_declarations_stack_guard()
+ {
+ pars->current_declarations.pop_back();
+ }
+ };
+
+ std::vector<token> const* tokens = {};
+ std::deque<token>* generated_tokens = {};
+ int pos = 0;
+ std::string parse_kind = {};
+
+ // Keep track of the function bodies' locations - used to emit comments
+ // in the right pass (decide whether it's a comment that belongs with
+ // the declaration or is part of the definition)
+ struct function_body_extent {
+ lineno_t first;
+ lineno_t last;
+ auto operator<=>(function_body_extent const&) const = default;
+ auto operator<=>(int i) const { return first <=> i; }
+
+ function_body_extent( lineno_t f, lineno_t l ): first{f}, last{l} { }
+ };
+ mutable std::vector<function_body_extent> function_body_extents;
+ mutable bool is_function_body_extents_sorted = false;
+
+public:
+ auto is_within_function_body(source_position p) const
+ {
+ // Short circuit the empty case, so that the rest of the function
+ // can unconditionally decrement any non-.begin() iterator once
+ if (function_body_extents.empty()) {
+ return false;
+ }
+
+ // Ensure we are sorted
+ if (!is_function_body_extents_sorted) {
+ std::sort(
+ function_body_extents.begin(),
+ function_body_extents.end()
+ );
+ is_function_body_extents_sorted = true;
+ }
+
+ // Find the first entry that is beyond pos, and back up one to
+ // the last that could be a match; this also ensures iter is
+ // dereferenceable, not .end()
+ auto iter = std::lower_bound(
+ function_body_extents.begin(),
+ function_body_extents.end(),
+ p.lineno+1
+ );
+ if (iter != function_body_extents.begin()) {
+ --iter;
+ }
+
+ // Now go backwards through the preceding entries until
+ // one includes pos or we move before pos
+ while (
+ iter->first <= p.lineno
+ )
+ {
+ if (
+ iter->first <= p.lineno
+ && p.lineno <= iter->last
+ )
+ {
+ return true;
+ }
+ if (iter == function_body_extents.begin()) {
+ break;
+ }
+ --iter;
+ }
+ return false;
+ }
+
+
+public:
+ //-----------------------------------------------------------------------
+ // Constructors - the copy constructor constructs a new instance with
+ // the same errors reference but otherwise a clean slate
+ //
+ // errors error list
+ //
+ parser( std::vector<error_entry>& errors_ )
+ : errors{ errors_ }
+ , parse_tree{std::make_unique<translation_unit_node>()}
+ { }
+
+ parser( parser const& that )
+ : errors{ that.errors }
+ , parse_tree{std::make_unique<translation_unit_node>()}
+ { }
+
+
+ //-----------------------------------------------------------------------
+ // parse
+ //
+ // tokens input tokens for this section of Cpp2 source code
+ // generated_tokens a shared place to store generated tokens
+ //
+ // Each call parses this section's worth of tokens and adds the
+ // result to the stored parse tree. Call this repeatedly for the Cpp2
+ // sections in a TU to build the whole TU's parse tree
+ //
+ auto parse(
+ std::vector<token> const& tokens_,
+ std::deque<token>& generated_tokens_
+ )
+ -> bool
+ {
+ parse_kind = "source file";
+
+ // Set per-parse state for the duration of this call
+ tokens = &tokens_;
+ generated_tokens = &generated_tokens_;
+
+ // Generate parse tree for this section as if a standalone TU
+ pos = 0;
+ auto tu = translation_unit();
+
+ // Then add it to the complete parse tree
+ parse_tree->declarations.insert(
+ parse_tree->declarations.end(),
+ std::make_move_iterator(tu->declarations.begin()),
+ std::make_move_iterator(tu->declarations.end())
+ );
+ if (!done()) {
+ error("unexpected text at end of Cpp2 code section", true, {}, true);
+ return false;
+ }
+ return true;
+ }
+
+
+ //-----------------------------------------------------------------------
+ // parse_one_statement
+ //
+ // tokens input tokens for this section of Cpp2 source code
+ // generated_tokens a shared place to store generated tokens
+ //
+ // Each call parses one statement and returns its parse tree.
+ //
+ auto parse_one_declaration(
+ std::vector<token> const& tokens_,
+ std::deque<token>& generated_tokens_
+ )
+ -> std::unique_ptr<statement_node>
+ {
+ parse_kind = "source string during code generation";
+
+ // Set per-parse state for the duration of this call
+ tokens = &tokens_;
+ generated_tokens = &generated_tokens_;
+
+ try {
+ // Parse one declaration - we succeed if the parse succeeded,
+ // and there were no new errors, and all tokens were consumed
+ auto errors_size = std::ssize(errors);
+ pos = 0;
+ if (auto d = statement();
+ d
+ && std::ssize(errors) == errors_size
+ && done()
+ )
+ {
+ return d;
+ }
+ }
+ catch(std::runtime_error& e) {
+ error(e.what(), true, {}, true);
+ }
+
+ return {};
+ }
+
+
+ //-----------------------------------------------------------------------
+ // Get a set of pointers to just the declarations in the given token map section
+ //
+ auto get_parse_tree_declarations_in_range(std::vector<token> const& token_range) const
+ -> std::vector< declaration_node const* >
+ {
+ assert (parse_tree);
+ assert (!token_range.empty());
+ auto first_line = token_range.front().position().lineno;
+ auto last_line = token_range.back().position().lineno;
+
+ auto ret = std::vector< declaration_node const* >{};
+ for (auto& decl : parse_tree->declarations)
+ {
+ assert(decl);
+
+ // The grammar and the tokens are in lineno order, so we don't
+ // need to look further once we pass the last lineno
+ if (decl->position().lineno > last_line) {
+ break;
+ }
+ if (decl->position().lineno >= first_line) {
+ ret.push_back( decl.get() );
+ }
+ }
+
+ return ret;
+ }
+
+
+ //-----------------------------------------------------------------------
+ // visit
+ //
+ auto visit(auto& v) -> void
+ {
+ parse_tree->visit(v, 0);
+ }
+
+private:
+ //-----------------------------------------------------------------------
+ // Error reporting: Fed into the supplied this->errors object
+ //
+ // msg message to be printed
+ //
+ // include_curr_token in this file (during parsing), we normally want
+ // to show the current token as the unexpected text
+ // we encountered, but some sema rules are applied
+ // early during parsing and for those it doesn't
+ // make sense to show the next token (e.g., when
+ // we detect and reject a "std::move" qualified-id,
+ // it's not relevant to add "at LeftParen: ("
+ // just because ( happens to be the next token)
+ //
+ auto error(
+ char const* msg,
+ bool include_curr_token = true,
+ source_position err_pos = {},
+ bool fallback = false
+ ) const
+ -> void
+ {
+ auto m = std::string{msg};
+ auto i = done() ? -1 : 0;
+ assert (peek(i));
+ if (include_curr_token) {
+ m += std::string(" (at '") + peek(i)->to_string() + "')";
+ }
+ if (
+ err_pos == source_position{}
+ ) {
+ err_pos = peek(i)->position();
+ }
+ errors.emplace_back( err_pos, m, false, fallback );
+ }
+
+ auto error(
+ std::string const& msg,
+ bool include_curr_token = true,
+ source_position err_pos = {},
+ bool fallback = false
+ ) const
+ -> void
+ {
+ error( msg.c_str(), include_curr_token, err_pos, fallback );
+ }
+
+ bool has_error() {
+ return !errors.empty();
+ }
+
+
+ //-----------------------------------------------------------------------
+ // Token navigation: Only these functions should access this->token_
+ //
+ auto curr() const
+ -> token const&
+ {
+ if (done()) {
+ throw std::runtime_error("unexpected end of " + parse_kind);
+ }
+
+ return (*tokens)[pos];
+ }
+
+ auto peek(int num) const
+ -> token const*
+ {
+ assert (tokens);
+ if (
+ pos + num >= 0
+ && pos + num < std::ssize(*tokens)
+ )
+ {
+ return &(*tokens)[pos + num];
+ }
+ return {};
+ }
+
+ auto done() const
+ -> bool
+ {
+ assert (tokens);
+ assert (pos <= std::ssize(*tokens));
+ return pos == std::ssize(*tokens);
+ }
+
+ auto next(int num = 1)
+ -> void
+ {
+ assert (tokens);
+ pos = std::min( pos+num, _as<int>(std::ssize(*tokens)) );
+ }
+
+
+ //-----------------------------------------------------------------------
+ // Parsers for unary expressions
+ //
+
+ //G primary-expression:
+ //G inspect-expression
+ //G id-expression
+ //G literal
+ //G '(' expression-list ')'
+ //GT '{' expression-list '}'
+ //G unnamed-declaration
+ //G
+ auto primary_expression()
+ -> std::unique_ptr<primary_expression_node>
+ {
+ auto n = std::make_unique<primary_expression_node>();
+
+ if (auto inspect = inspect_expression(true))
+ {
+ n->expr = std::move(inspect);
+ return n;
+ }
+
+ if (auto id = id_expression()) {
+ n->expr = std::move(id);
+ return n;
+ }
+
+ if (auto lit = literal()) {
+ n->expr = std::move(lit);
+ return n;
+ }
+
+ if (curr().type() == lexeme::LeftParen
+ // If in the future (not now) we decide to allow braced-expressions
+ // || curr().type() == lexeme::LeftBrace
+ )
+ {
+ bool inside_initializer = (
+ peek(-1) && peek(-1)->type() == lexeme::Assignment
+ );
+ auto open_paren = &curr();
+ auto close = close_paren_type(open_paren->type());
+ auto close_text = [&] () -> std::string { if (close == lexeme::RightParen) { return ")"; } return "}"; }();
+ next();
+ auto expr_list = expression_list(open_paren, inside_initializer);
+ if (!expr_list) {
+ error("unexpected text - ( is not followed by an expression-list");
+ next();
+ return {};
+ }
+ if (curr().type() != close_paren_type(open_paren->type())) {
+ error("unexpected text - expression-list is not terminated by " + close_text);
+ next();
+ return {};
+ }
+ expr_list->close_paren = &curr();
+ next();
+ if (
+ curr().type() != lexeme::Semicolon
+ && curr().type() != lexeme::RightParen
+ && curr().type() != lexeme::RightBracket
+ && curr().type() != lexeme::Greater
+ && curr().type() != lexeme::Comma
+ ) {
+ expr_list->inside_initializer = false;
+ }
+ n->expression_list_is_fold_expression = expr_list->is_fold_expression();
+ n->expr = std::move(expr_list);
+ return n;
+ }
+
+ if (auto decl = unnamed_declaration(curr().position(), false, true)) // captures are allowed
+ {
+ assert (
+ !decl->has_name()
+ && "ICE: declaration should have been unnamed"
+ );
+
+ if (auto obj = std::get_if<declaration_node::an_object>(&decl->type)) {
+ if ((*obj)->is_wildcard()) {
+ error("an unnamed object at expression scope currently cannot have a deduced type (the reason to create an unnamed object is typically to create a temporary of a named type)");
+ next();
+ return {};
+ }
+ }
+ else if (auto func = std::get_if<declaration_node::a_function>(&decl->type)) {
+ if ((*func)->returns.index() == function_type_node::list) {
+ error("an unnamed function at expression scope currently cannot return multiple values");
+ next();
+ return {};
+ }
+ if ( // check if a single-expression function is followed by an extra second semicolon
+ decl->initializer && decl->initializer->is_expression()
+ && !done() && curr().type() == lexeme::Semicolon
+ ) {
+ error("a single-expression function should end with a single semicolon");
+ }
+ if (!(*func)->contracts.empty()) {
+ error("an unnamed function at expression scope currently cannot have contracts");
+ next();
+ return {};
+ }
+ }
+ else {
+ error("(temporary alpha limitation) an unnamed declaration at expression scope must be a function or an object");
+ next();
+ return {};
+ }
+
+ if (
+ peek(-1) && peek(-1)->type() != lexeme::RightBrace // it is not a braced function expression
+ && curr().type() != lexeme::LeftParen // not imediatelly called
+ && curr().type() != lexeme::RightParen // not as a last argument to function
+ && curr().type() != lexeme::Comma // not as first or in-the-middle, function argument
+ && curr().type() != lexeme::Greater // not as the last argument to template
+ && curr().type() != lexeme::RightBracket // not as the last index argument
+ && curr() != "is" // not as the argument to is
+ && curr() != "as" // not as the argument to as
+ && curr() != "do" // not as `for`'s `next`.
+ ) {
+ // this is a fix for a short function syntax that should have double semicolon used
+ // (check comment in expression_statement(bool semicolon_required))
+ // We simulate double semicolon by moving back to single semicolon.
+ next(-1);
+ }
+
+ n->expr = std::move(decl);
+ return n;
+ }
+
+ return {};
+ }
+
+
+ //G postfix-expression:
+ //G primary-expression
+ //G postfix-expression postfix-operator [Note: without whitespace before the operator]
+ //G postfix-expression '[' expression-list? ']'
+ //G postfix-expression '(' expression-list? ')'
+ //G postfix-expression '.' id-expression
+ //G
+ auto postfix_expression()
+ -> std::unique_ptr<postfix_expression_node>
+ {
+ auto n = std::make_unique<postfix_expression_node>();
+ n->expr = primary_expression();
+ if (!(n->expr)) {
+ return {};
+ }
+
+ while (
+ !done()
+ && (
+ (is_postfix_operator(curr().type())
+ // Postfix operators must be lexically adjacent
+ && curr().position().lineno == peek(-1)->position().lineno
+ && curr().position().colno == peek(-1)->position().colno + peek(-1)->length()
+ )
+ || curr().type() == lexeme::LeftBracket
+ || curr().type() == lexeme::LeftParen
+ || curr().type() == lexeme::Dot
+ )
+ )
+ {
+ // these can't be unary operators if followed by a (, identifier, or literal
+ if (
+ (
+ curr().type() == lexeme::Multiply
+ || curr().type() == lexeme::Ampersand
+ || curr().type() == lexeme::Tilde
+ )
+ && peek(1)
+ && (
+ peek(1)->type() == lexeme::LeftParen
+ || peek(1)->type() == lexeme::Identifier
+ || is_literal(peek(1)->type())
+ )
+ )
+ {
+ auto op = curr().to_string();
+ auto msg = "postfix unary " + op;
+ if (curr().type() == lexeme::Multiply ) { msg += " (dereference)" ; }
+ else if (curr().type() == lexeme::Ampersand) { msg += " (address-of)" ; }
+ else if (curr().type() == lexeme::Tilde ) { msg += " (unary bit-complement)" ; }
+ msg += " cannot be immediately followed by a (, identifier, or literal - add whitespace before "
+ + op + " here if you meant binary " + op;
+ if (curr().type() == lexeme::Multiply ) { msg += " (multiplication)" ; }
+ else if (curr().type() == lexeme::Ampersand) { msg += " (bitwise and)" ; }
+ else if (curr().type() == lexeme::Tilde ) { msg += " (binary bit-complement)"; }
+
+ error(msg, false);
+ break;
+ }
+
+ if (curr().type() == lexeme::Dollar) {
+ // cap_grp must not already be set, or this is a multi-$ postfix-expression
+ if (n->cap_grp) {
+ error("$ (capture) can appear at most once in a single postfix-expression");
+ return {};
+ }
+ if (current_capture_groups.empty()) {
+ error("$ (capture) cannot appear here - it must appear in an anonymous expression function, a postcondition, or an interpolated string literal");
+ return {};
+ }
+ n->cap_grp = current_capture_groups.back();
+ n->cap_grp->add(n.get());
+ }
+
+ // Remember current position, in case we need to backtrack
+ auto term_pos = pos;
+
+ auto term = postfix_expression_node::term{&curr()};
+ next();
+
+ if (term.op->type() == lexeme::LeftBracket)
+ {
+ term.expr_list = expression_list(term.op);
+ if (!term.expr_list)
+ {
+ error("[ is not followed by a valid expression list");
+ return {};
+ }
+ if (curr().type() != lexeme::RightBracket)
+ {
+ error("unexpected text - [ is not properly matched by ]", true, {}, true);
+ return {};
+ }
+ term.expr_list->close_paren = &curr();
+ term.op_close = &curr();
+ next();
+ }
+ else if (term.op->type() == lexeme::LeftParen)
+ {
+ // Next should be an expression-list followed by a ')'
+ // If not, then this wasn't a call expression so backtrack to
+ // the '(' which will be part of the next grammar production
+
+ term.expr_list = expression_list(term.op);
+ if (
+ term.expr_list
+ && curr().type() == lexeme::RightParen
+ )
+ {
+ term.expr_list->close_paren = &curr();
+ term.op_close = &curr();
+ next();
+ }
+ else
+ {
+ pos = term_pos; // backtrack
+ break;
+ }
+ }
+ else if (term.op->type() == lexeme::Dot)
+ {
+ term.id_expr = id_expression();
+ if (!term.id_expr) {
+ error("'.' must be followed by a valid member name");
+ return {};
+ }
+ }
+
+ n->ops.push_back( std::move(term) );
+ }
+
+ if (auto tok = n->expr->get_token();
+ tok
+ && *tok == "this"
+ && curr().type() == lexeme::Arrow
+ )
+ {
+ auto next_word = std::string{};
+ if (peek(1)) {
+ next_word = peek(1)->to_string();
+ }
+ error("'this' is not a pointer - write 'this." + next_word + "' instead of 'this->" + next_word + "'");
+ return {};
+ }
+
+ for (auto& e : expression_node::current_expressions) {
+ e->num_subexpressions += std::ssize(n->ops);
+ }
+
+ return n;
+ }
+
+
+ //G prefix-expression:
+ //G postfix-expression
+ //G prefix-operator prefix-expression
+ //GTODO await-expression
+ //GTODO 'sizeof' '(' type-id ')'
+ //GTODO 'sizeof' '...' ( identifier ')'
+ //GTODO 'alignof' '(' type-id ')'
+ //GTODO throws-expression
+ //G
+ auto prefix_expression()
+ -> std::unique_ptr<prefix_expression_node>
+ {
+ auto n = std::make_unique<prefix_expression_node>();
+ for ( ;
+ is_prefix_operator(curr());
+ next()
+ )
+ {
+ n->ops.push_back(&curr());
+ }
+ if ((n->expr = postfix_expression())) {
+ return n;
+ }
+ switch (curr().type())
+ {
+ break; case lexeme::PlusPlus:
+ error("prefix '++var' is not valid Cpp2; use postfix 'var++' instead", false);
+ break; case lexeme::MinusMinus:
+ error("prefix '--var' is not valid Cpp2; use postfix 'var--' instead", false);
+ break; case lexeme::Multiply:
+ error("prefix '*ptr' dereference is not valid Cpp2; use postfix 'ptr*' instead", false);
+ break; case lexeme::Ampersand:
+ error("prefix '&var' address-of is not valid Cpp2; use postfix 'var&' instead", false);
+ break; case lexeme::Tilde:
+ error("prefix '~var' is not valid Cpp2; use postfix 'var~' instead", false);
+ break; default: ;
+ }
+ return {};
+ }
+
+
+ //-----------------------------------------------------------------------
+ // Parsers for binary expressions
+ //
+
+ // The general /*binary*/-expression:
+ // /*term*/-expression { { /* operators at this precedence level */ } /*term*/-expression }*
+ //
+ template<
+ typename Binary,
+ typename ValidateOp,
+ typename TermFunc
+ >
+ auto binary_expression(
+ ValidateOp validate_op,
+ TermFunc term
+ )
+ -> std::unique_ptr<Binary>
+ {
+ auto n = std::make_unique<Binary>();
+ if ( (n->expr = term()) )
+ {
+ while (!done())
+ {
+ typename Binary::term t{};
+
+ // Remember current position, because we may need to backtrack if this next
+ // t.op might be valid but isn't followed by a valid term and so isn't for us
+ auto term_pos = pos;
+
+ // Most of these predicates only look at the current token and return
+ // true/false == whether this is a valid operator for this production
+ if constexpr( requires{ bool{ validate_op(curr()) }; } ) {
+ if (!validate_op(curr())) {
+ break;
+ }
+ t.op = &curr();
+ next();
+ }
+
+ // But for shift-expression we may synthesize >> from > >
+ // which will return a token* == a valid operator for this production
+ // (possibly a synthesized new token) or nullptr otherwise
+ else if constexpr( requires{ validate_op(curr(), *peek(1)); } ) {
+ if (
+ peek(1) == nullptr
+ || (t.op = validate_op(curr(), *peek(1))) == nullptr
+ )
+ {
+ break;
+ }
+ // If we didn't consume the next token, we consumed the next two
+ if (t.op != &curr()) {
+ next();
+ }
+ next();
+ }
+
+ // And it shouldn't be anything else
+ else {
+ assert (!"ICE: validate_op should take one token and return bool, or two tokens and return token const* ");
+ }
+
+ // At this point we may have a valid t.op, so try to parse the next term...
+ // If it's not a valid term, then this t.op wasn't for us, pop it and return
+ // what we found (e.g., with "requires expression = {...}" the = is a grammar
+ // element and not an operator, it isn't and can't be part of the expression)
+ if ( !(t.expr = term()) ) {
+ pos = term_pos; // backtrack
+ return n;
+ }
+
+ // We got a term, so this op + term was for us
+ n->terms.push_back( std::move(t) );
+ }
+ return n;
+ }
+ return {};
+ }
+
+ //G multiplicative-expression:
+ //G is-as-expression
+ //G multiplicative-expression '*' is-as-expression
+ //G multiplicative-expression '/' is-as-expression
+ //G multiplicative-expression '%' is-as-expression
+ //G
+ auto multiplicative_expression()
+ -> auto
+ {
+ return binary_expression<multiplicative_expression_node> (
+ [](token const& t){ return t.type() == lexeme::Multiply || t.type() == lexeme::Slash || t.type() == lexeme::Modulo; },
+ [this]{ return is_as_expression(); }
+ );
+ }
+
+ //G additive-expression:
+ //G multiplicative-expression
+ //G additive-expression '+' multiplicative-expression
+ //G additive-expression '-' multiplicative-expression
+ //G
+ auto additive_expression()
+ -> auto
+ {
+ return binary_expression<additive_expression_node> (
+ [](token const& t){ return t.type() == lexeme::Plus || t.type() == lexeme::Minus; },
+ [this]{ return multiplicative_expression(); }
+ );
+ }
+
+ //G shift-expression:
+ //G additive-expression
+ //G shift-expression '<<' additive-expression
+ //G shift-expression '>>' additive-expression
+ //G
+ auto shift_expression(bool allow_angle_operators = true)
+ -> auto
+ {
+ if (allow_angle_operators) {
+ return binary_expression<shift_expression_node> (
+ [this](token const& t, token const& next) -> token const* {
+ if (t.type() == lexeme::LeftShift) {
+ return &t;
+ }
+ if (
+ t.type() == lexeme::Greater
+ && next.type() == lexeme::Greater
+ && t.position() == source_position{ next.position().lineno, next.position().colno-1 }
+ )
+ {
+ generated_tokens->emplace_back( ">>", t.position(), lexeme::RightShift);
+ return &generated_tokens->back();
+ }
+ return nullptr;
+ },
+ [this]{ return additive_expression(); }
+ );
+ }
+ else {
+ return binary_expression<shift_expression_node> (
+ [](token const&, token const&) -> token const* { return nullptr; },
+ [this]{ return additive_expression(); }
+ );
+ }
+ }
+
+ //G compare-expression:
+ //G shift-expression
+ //G compare-expression '<=>' shift-expression
+ //G
+ auto compare_expression(bool allow_angle_operators = true)
+ -> auto
+ {
+ return binary_expression<compare_expression_node> (
+ [](token const& t){ return t.type() == lexeme::Spaceship; },
+ [=,this]{ return shift_expression(allow_angle_operators); }
+ );
+ }
+
+ //G relational-expression:
+ //G compare-expression
+ //G relational-expression '<' compare-expression
+ //G relational-expression '>' compare-expression
+ //G relational-expression '<=' compare-expression
+ //G relational-expression '>=' compare-expression
+ //G
+ auto relational_expression(bool allow_angle_operators = true)
+ -> auto
+ {
+ if (allow_angle_operators) {
+ return binary_expression<relational_expression_node> (
+ [](token const& t, token const& next) -> token const* {
+ if (
+ t.type() == lexeme::Less
+ || t.type() == lexeme::LessEq
+ || (t.type() == lexeme::Greater && next.type() != lexeme::GreaterEq)
+ || t.type() == lexeme::GreaterEq
+ ) {
+ return &t;
+ }
+ return nullptr;
+ },
+ [=,this]{ return compare_expression(allow_angle_operators); }
+ );
+ }
+ else {
+ return binary_expression<relational_expression_node> (
+ [](token const&, token const&) -> token const* { return nullptr; },
+ [=,this]{ return compare_expression(allow_angle_operators); }
+ );
+ }
+ }
+
+ //G equality-expression:
+ //G relational-expression
+ //G equality-expression '==' relational-expression
+ //G equality-expression '!=' relational-expression
+ //G
+ auto equality_expression(bool allow_angle_operators = true, bool allow_equality = true)
+ -> auto
+ {
+ if (allow_equality) {
+ return binary_expression<equality_expression_node> (
+ [](token const& t){ return t.type() == lexeme::EqualComparison || t.type() == lexeme::NotEqualComparison; },
+ [=,this]{ return relational_expression(allow_angle_operators); }
+ );
+ }
+ else {
+ return binary_expression<equality_expression_node> (
+ [](token const& t){ return t.type() == lexeme::NotEqualComparison; },
+ [=,this]{ return relational_expression(allow_angle_operators); }
+ );
+ }
+ }
+
+ //G bit-and-expression:
+ //G equality-expression
+ //G bit-and-expression '&' equality-expression
+ //G
+ auto bit_and_expression(bool allow_angle_operators = true, bool allow_equality = true)
+ -> auto
+ {
+ return binary_expression<bit_and_expression_node> (
+ [](token const& t){ return t.type() == lexeme::Ampersand; },
+ [=,this]{ return equality_expression(allow_angle_operators, allow_equality); }
+ );
+ }
+
+ //G bit-xor-expression:
+ //G bit-and-expression
+ //G bit-xor-expression '^' bit-and-expression
+ //G
+ auto bit_xor_expression(bool allow_angle_operators = true, bool allow_equality = true)
+ -> auto
+ {
+ return binary_expression<bit_xor_expression_node> (
+ [](token const& t){ return t.type() == lexeme::Caret; },
+ [=,this]{ return bit_and_expression(allow_angle_operators, allow_equality); }
+ );
+ }
+
+ //G bit-or-expression:
+ //G bit-xor-expression
+ //G bit-or-expression '|' bit-xor-expression
+ //G
+ auto bit_or_expression(bool allow_angle_operators = true, bool allow_equality = true)
+ -> auto
+ {
+ return binary_expression<bit_or_expression_node> (
+ [](token const& t){ return t.type() == lexeme::Pipe; },
+ [=,this]{ return bit_xor_expression(allow_angle_operators, allow_equality); }
+ );
+ }
+
+ //G logical-and-expression:
+ //G bit-or-expression
+ //G logical-and-expression '&&' bit-or-expression
+ //G
+ auto logical_and_expression(bool allow_angle_operators = true, bool allow_equality = true)
+ -> auto
+ {
+ return binary_expression<logical_and_expression_node> (
+ [](token const& t){ return t.type() == lexeme::LogicalAnd; },
+ [=,this]{ return bit_or_expression(allow_angle_operators, allow_equality); }
+ );
+ }
+
+ // constant-expression: // don't need intermediate production, just use:
+ // conditional-expression: // don't need intermediate production, just use:
+ //G logical-or-expression:
+ //G logical-and-expression
+ //G logical-or-expression '||' logical-and-expression
+ //G
+ auto logical_or_expression(bool allow_angle_operators = true, bool allow_equality = true)
+ -> auto
+ {
+ return binary_expression<logical_or_expression_node> (
+ [](token const& t){ return t.type() == lexeme::LogicalOr; },
+ [=,this]{ return logical_and_expression(allow_angle_operators, allow_equality); }
+ );
+ }
+
+ //G assignment-expression:
+ //G logical-or-expression
+ //G assignment-expression assignment-operator logical-or-expression
+ //G
+ auto assignment_expression(
+ bool allow_angle_operators = true
+ )
+ -> std::unique_ptr<assignment_expression_node>
+ {
+ auto ret = std::unique_ptr<assignment_expression_node>{};
+
+ if (allow_angle_operators)
+ {
+ ret = binary_expression<assignment_expression_node> (
+ [this](token const& t, token const& next) -> token const* {
+ if (is_assignment_operator(t.type())) {
+ return &t;
+ }
+ if (
+ t.type() == lexeme::Greater
+ && next.type() == lexeme::GreaterEq
+ && t.position() == source_position{ next.position().lineno, next.position().colno-1 }
+ )
+ {
+ generated_tokens->emplace_back( ">>=", t.position(), lexeme::RightShiftEq);
+ return &generated_tokens->back();
+ }
+ return nullptr;
+ },
+ [=,this]{
+ return logical_or_expression(allow_angle_operators);
+ }
+ );
+ }
+ else
+ {
+ ret = binary_expression<assignment_expression_node> (
+ [](token const&, token const&) -> token const* { return nullptr; },
+ [=,this]{
+ return logical_or_expression(allow_angle_operators);
+ }
+ );
+ }
+
+ if (ret && ret->terms_size() > 1) {
+ error("assignment cannot be chained - instead of 'c = b = a;', write 'b = a; c = b;'", false);
+ return {};
+ }
+
+ return ret;
+ }
+
+ //G expression: // eliminated 'condition:' - just use 'expression:'
+ //G assignment-expression
+ //GTODO try expression
+ //G
+ auto expression(
+ bool allow_angle_operators = true,
+ bool check_arrow = true
+ )
+ -> std::unique_ptr<expression_node>
+ {
+ auto n = std::make_unique<expression_node>();
+
+ {
+ expression_node::current_expressions.push_back(n.get());
+ auto guard = finally([&]{ expression_node::current_expressions.pop_back(); });
+
+ if (!(n->expr = assignment_expression(allow_angle_operators))) {
+ return {};
+ }
+
+ if (
+ check_arrow
+ && !done()
+ && curr().type() == lexeme::Arrow
+ )
+ {
+ error("'->' is not Cpp2 deference syntax - write '*.' instead");
+ return {};
+ }
+ }
+
+ for (auto& e : expression_node::current_expressions) {
+ ++e->num_subexpressions;
+ }
+ return n;
+ }
+
+ //G expression-list:
+ //G parameter-direction? expression
+ //G expression-list ',' parameter-direction? expression
+ //G
+ auto expression_list(
+ token const* open_paren,
+ bool inside_initializer = false
+ )
+ -> std::unique_ptr<expression_list_node>
+ {
+ auto pass = passing_style::in;
+ auto n = std::make_unique<expression_list_node>();
+ n->open_paren = open_paren;
+ n->inside_initializer = inside_initializer;
+
+ if (auto dir = to_passing_style(curr());
+ (
+ dir == passing_style::out
+ || dir == passing_style::move
+ || dir == passing_style::forward
+ )
+ && peek(1)
+ && peek(1)->type() == lexeme::Identifier
+ )
+ {
+ pass = dir;
+ next();
+ }
+ auto x = expression();
+
+ // If this is an empty expression_list, we're done
+ if (!x) {
+ return n;
+ }
+
+ // Otherwise remember the first expression
+ n->expressions.push_back( { pass, std::move(x) } );
+ // and see if there are more...
+ while (curr().type() == lexeme::Comma) {
+ next();
+ pass = passing_style::in;
+ if (auto dir = to_passing_style(curr());
+ dir == passing_style::out
+ || dir == passing_style::move
+ || dir == passing_style::forward
+ )
+ {
+ pass = dir;
+ next();
+ }
+ auto expr = expression();
+ if (!expr) {
+ error("invalid text in expression list", true, {}, true);
+ return {};
+ }
+ n->expressions.push_back( { pass, std::move(expr) } );
+ }
+ return n;
+ }
+
+
+ //G type-id:
+ //G type-qualifier-seq? qualified-id
+ //G type-qualifier-seq? unqualified-id
+ //G
+ //G type-qualifier-seq:
+ //G type-qualifier
+ //G type-qualifier-seq type-qualifier
+ //G
+ //G type-qualifier:
+ //G 'const'
+ //G '*'
+ //G
+ auto type_id()
+ -> std::unique_ptr<type_id_node>
+ {
+ auto n = std::make_unique<type_id_node>();
+
+ while (
+ (curr().type() == lexeme::Keyword && curr() == "const")
+ || curr().type() == lexeme::Multiply
+ )
+ {
+ if (
+ curr() == "const"
+ && !n->pc_qualifiers.empty()
+ && *n->pc_qualifiers.back() == "const"
+ )
+ {
+ error("consecutive 'const' not allowed");
+ return {};
+ }
+ n->pc_qualifiers.push_back( &curr() );
+ next();
+ }
+
+ if (auto id = qualified_id()) {
+ n->pos = id->position();
+ n->id = std::move(id);
+ assert (n->id.index() == type_id_node::qualified);
+ }
+ else if (auto id = unqualified_id()) {
+ n->pos = id->position();
+ n->id = std::move(id);
+ assert (n->id.index() == type_id_node::unqualified);
+ }
+ else {
+ if (!n->pc_qualifiers.empty()) {
+ error("'*'/'const' type qualifiers must be followed by a type name or '_' wildcard");
+ }
+ return {};
+ }
+ if (curr().type() == lexeme::Multiply) {
+ error("'T*' is not a valid Cpp2 type; use '*T' for a pointer instead", false);
+ return {};
+ }
+
+ return n;
+ }
+
+
+ //G is-as-expression:
+ //G prefix-expression
+ //G is-as-expression is-type-constraint
+ //G is-as-expression is-value-constraint
+ //G is-as-expression as-type-cast
+ //GTODO type-id is-type-constraint
+ //G
+ //G is-type-constraint
+ //G 'is' type-id
+ //G
+ //G is-value-constraint
+ //G 'is' expression
+ //G
+ //G as-type-cast
+ //G 'as' type-id
+ //G
+ auto is_as_expression()
+ -> std::unique_ptr<is_as_expression_node>
+ {
+ auto n = std::make_unique<is_as_expression_node>();
+ n->expr = prefix_expression();
+ if (!(n->expr)) {
+ return {};
+ }
+
+ auto is_found = false;
+ auto as_found = false;
+
+ while (
+ !done()
+ && (curr() == "is" || curr() == "as")
+ )
+ {
+ if (curr() == "is") {
+ if (is_found) {
+ error("repeated 'is' are not allowed");
+ return {};
+ }
+ is_found = true;
+ }
+ else {
+ as_found = true;
+ }
+
+ if (is_found && as_found) {
+ error("mixed 'is' and 'as' are not allowed");
+ return {};
+ }
+
+ auto term = is_as_expression_node::term{};
+ term.op = &curr();
+ next();
+
+ if ((term.type = type_id()) != nullptr) {
+ ;
+ }
+ else if ((term.expr = expression()) != nullptr) {
+ ;
+ }
+
+ if (
+ *term.op == "as"
+ && term.expr
+ )
+ {
+ error("'as' must be followed by a type-id, not an expression", false);
+ return {};
+ }
+ if (
+ !term.type
+ && !term.expr
+ )
+ {
+ if (*term.op == "is") {
+ error( "'is' must be followed by a type-id or an expression", false);
+ }
+ else {
+ error( "'as' must be followed by a type-id", false);
+ }
+ return {};
+ }
+
+ n->ops.push_back( std::move(term) );
+ }
+
+ return n;
+ }
+
+
+ //G unqualified-id:
+ //G identifier
+ //G keyword
+ //G template-id
+ //GTODO operator-function-id
+ //G ...
+ //G
+ //G template-id:
+ //G identifier '<' template-argument-list? '>'
+ //G
+ //G template-argument-list:
+ //G template-argument-list ',' template-argument
+ //G
+ //G template-argument:
+ //G # note: < > << >> are not allowed in expressions until new ( is opened
+ //G 'const' type-id
+ //G expression
+ //G type-id
+ //G
+ auto unqualified_id()
+ -> std::unique_ptr<unqualified_id_node>
+ {
+ // Handle the identifier
+ if (
+ curr().type() != lexeme::Identifier
+ && curr().type() != lexeme::Keyword
+ && curr().type() != lexeme::Cpp2FixedType
+ && curr().type() != lexeme::Ellipsis
+ )
+ {
+ return {};
+ }
+
+ auto n = std::make_unique<unqualified_id_node>();
+
+ n->identifier = &curr();
+ auto one_past_identifier_end_pos = curr().position();
+ one_past_identifier_end_pos.colno += curr().length();
+ next();
+
+ // Handle the template-argument-list if there is one
+ if (
+ curr().type() == lexeme::Less
+ && curr().position() == one_past_identifier_end_pos
+ )
+ {
+ // Remember current position, in case this < is isn't a template argument list
+ auto start_pos = pos;
+
+ n->open_angle = curr().position();
+ next();
+
+ auto term = template_argument{};
+
+ do {
+ // If it doesn't start with * or const (which can only be a type id),
+ // try parsing it as an expression
+ if (auto e = [&]{
+ if (
+ curr().type() == lexeme::Multiply // '*'
+ || curr() == "const" // 'const'
+ )
+ {
+ return decltype(expression()){};
+ }
+ return expression(false); // false == disallow unparenthesized relational comparisons in template args
+ }()
+ )
+ {
+ term.arg = std::move(e);
+ }
+
+ // Else try parsing it as a type id
+ else if (auto i = type_id()) {
+ term.arg = std::move(i);
+ }
+
+ // Else if we already got at least one template-argument, this is a
+ // ',' followed by something that isn't a valid template-arg
+ else if (std::ssize(n->template_args) > 0) {
+ error( "expected a template argument after ','", false);
+ return {};
+ }
+
+ // Else this is an empty '<>' list which is okay
+ else {
+ break;
+ }
+
+ n->template_args.push_back( std::move(term) );
+ }
+ // Use the lambda trick to jam in a "next" clause
+ while (
+ curr().type() == lexeme::Comma
+ && [&]{term.comma = curr().position(); next(); return true;}()
+ );
+ // When this is rewritten in Cpp2, it will be:
+ // while curr().type() == lexeme::Comma
+ // next term.comma = curr().position();
+
+ if (curr().type() != lexeme::Greater) {
+ // Aha, this wasn't a template argument list after all,
+ // so back out just that part and return the identifier
+ n->open_angle = source_position{};
+ n->template_args.clear();
+ pos = start_pos;
+ return n;
+ }
+ n->close_angle = curr().position();
+ next();
+ }
+
+ else {
+ if (*n->identifier == "new") {
+ error( "use 'new<" + curr().to_string() + ">', not 'new " + curr().to_string() + "'", false);
+ return {};
+ }
+ if (*n->identifier == "co_await" || *n->identifier == "co_yield") {
+ error( "(temporary alpha limitation) coroutines are not yet supported in Cpp2", false);
+ return {};
+ }
+ }
+
+ return n;
+ }
+
+
+ //G qualified-id:
+ //G nested-name-specifier unqualified-id
+ //G member-name-specifier unqualified-id
+ //G
+ //G nested-name-specifier:
+ //G '::'
+ //G unqualified-id '::'
+ //G
+ //G member-name-specifier:
+ //G unqualified-id '.'
+ //G
+ auto qualified_id()
+ -> std::unique_ptr<qualified_id_node>
+ {
+ auto n = std::make_unique<qualified_id_node>();
+
+ auto term = qualified_id_node::term{nullptr};
+
+ // Handle initial :: if present, else the first scope_op will be null
+ if (curr().type() == lexeme::Scope) {
+ term.scope_op = &curr();
+ next();
+ }
+
+ // Remember current position, because we need to look ahead to the next ::
+ auto start_pos = pos;
+
+ // If we don't get a first id, or if we didn't have a leading :: and
+ // the next thing isn't :: or ., back out and report unsuccessful
+ term.id = unqualified_id();
+ if (
+ !term.id
+ || (!term.scope_op && curr().type() != lexeme::Scope)
+ )
+ {
+ pos = start_pos; // backtrack
+ return {};
+ }
+
+ // Reject "std" :: "move" / "forward"
+ assert (term.id->identifier);
+ auto first_uid_was_std = (*term.id->identifier == "std");
+ auto first_time_through_loop = true;
+
+ n->ids.push_back( std::move(term) );
+
+ while (curr().type() == lexeme::Scope)
+ {
+ auto term = qualified_id_node::term{ &curr() };
+ next();
+ term.id = unqualified_id();
+ if (!term.id) {
+ error("invalid text in qualified name", true, {}, true);
+ return {};
+ }
+ assert (term.id->identifier);
+ if (
+ first_time_through_loop
+ && first_uid_was_std
+ && term.scope_op->type() == lexeme::Scope
+ )
+ {
+ if (*term.id->identifier == "move") {
+ error("std::move is not needed in Cpp2 - use 'move' parameters/arguments instead", false);
+ return {};
+ }
+ else if (*term.id->identifier == "forward") {
+ error("std::forward is not needed in Cpp2 - use 'forward' parameters/arguments instead", false);
+ return {};
+ }
+ first_time_through_loop = false;
+ }
+ n->ids.push_back( std::move(term) );
+ }
+
+ return n;
+ }
+
+
+ //G id-expression:
+ //G qualified-id
+ //G unqualified-id
+ //G
+ auto id_expression()
+ -> std::unique_ptr<id_expression_node>
+ {
+ auto n = std::make_unique<id_expression_node>();
+ if (auto id = qualified_id()) {
+ n->pos = id->position();
+ n->id = std::move(id);
+ assert (n->id.index() == id_expression_node::qualified);
+ return n;
+ }
+ if (auto id = unqualified_id()) {
+ n->pos = id->position();
+ n->id = std::move(id);
+ assert (n->id.index() == id_expression_node::unqualified);
+ return n;
+ }
+ return {};
+ }
+
+ //G literal:
+ //G integer-literal ud-suffix?
+ //G character-literal ud-suffix?
+ //G floating-point-literal ud-suffix?
+ //G string-literal ud-suffix?
+ //G boolean-literal ud-suffix?
+ //G pointer-literal ud-suffix?
+ //G user-defined-literal ud-suffix?
+ //G
+ auto literal()
+ -> std::unique_ptr<literal_node>
+ {
+ if (is_literal(curr().type())) {
+ auto n = std::make_unique<literal_node>();
+ n->literal = &curr();
+ next();
+ if (curr().type() == lexeme::UserDefinedLiteralSuffix) {
+ n->user_defined_suffix = &curr();
+ next();
+ }
+ return n;
+ }
+ return {};
+ }
+
+ //G expression-statement:
+ //G expression ';'
+ //G expression
+ //G
+ auto expression_statement(
+ bool semicolon_required
+ )
+ -> std::unique_ptr<expression_statement_node>
+ {
+ auto n = std::make_unique<expression_statement_node>();
+
+ expression_statement_node::current_expression_statements.push_back(n.get());
+ auto guard = finally([&]{ expression_statement_node::current_expression_statements.pop_back(); });
+
+ if (!(n->expr = expression(true, true))) {
+ return {};
+ }
+
+ if (
+ semicolon_required
+ && (done() || curr().type() != lexeme::Semicolon)
+ && peek(-1)->type() != lexeme::Semicolon
+ // this last peek(-1)-condition is a hack (? or is it just
+ // maybe elegant? I'm torn) so that code like
+ //
+ // callback := :(inout x:_) = x += "suffix"; ;
+ //
+ // doesn't need the redundant semicolon at the end of a decl...
+ // there's probably a cleaner way to do it, but this works and
+ // it doesn't destabilize any regression tests
+ )
+ {
+ return {};
+ }
+ if (
+ !done()
+ && curr().type() == lexeme::Semicolon
+ )
+ {
+ n->has_semicolon = true;
+ next();
+ }
+ return n;
+ }
+
+
+ //G selection-statement:
+ //G 'if' 'constexpr'? logical-or-expression compound-statement
+ //G 'if' 'constexpr'? logical-or-expression compound-statement 'else' compound-statement
+ //G
+ auto selection_statement()
+ -> std::unique_ptr<selection_statement_node>
+ {
+ if (
+ curr().type() != lexeme::Keyword
+ || curr() != "if"
+ )
+ {
+ return {};
+ }
+ auto n = std::make_unique<selection_statement_node>();
+ n->identifier = &curr();
+ next();
+
+ if (
+ curr().type() == lexeme::Keyword
+ && curr() == "constexpr"
+ )
+ {
+ n->is_constexpr = true;
+ next();
+ }
+
+ if (auto e = logical_or_expression()) {
+ n->expression = std::move(e);
+ }
+ else {
+ error("invalid if condition", true, {}, true);
+ return {};
+ }
+
+ if (curr().type() != lexeme::LeftBrace) {
+ error("an if branch body must be enclosed with { }");
+ return {};
+ }
+
+ if (auto s = compound_statement()) {
+ n->true_branch = std::move(s);
+ }
+ else {
+ error("invalid if branch body", true, {}, true);
+ return {};
+ }
+
+ if (
+ curr().type() != lexeme::Keyword
+ || curr() != "else"
+ )
+ {
+ // Add empty else branch to simplify processing elsewhere
+ // Note: Position (0,0) signifies it's implicit (no source location)
+ n->false_branch =
+ std::make_unique<compound_statement_node>( source_position(0,0) );
+ }
+ else {
+ n->else_pos = curr().position();
+ next();
+
+ if (
+ curr().type() != lexeme::LeftBrace
+ && curr() != "if"
+ )
+ {
+ error("an else branch body must be enclosed with { }");
+ return {};
+ }
+
+ if (auto s = compound_statement( source_position{}, true )) {
+ n->false_branch = std::move(s);
+ n->has_source_false_branch = true;
+ }
+ else {
+ error("invalid else branch body", true, {}, true);
+ return {};
+ }
+ }
+
+ return n;
+ }
+
+
+ //G return-statement:
+ //G return expression? ';'
+ //G
+ auto return_statement()
+ -> std::unique_ptr<return_statement_node>
+ {
+ if (
+ curr().type() != lexeme::Keyword
+ || curr() != "return"
+ )
+ {
+ return {};
+ }
+
+ auto n = std::make_unique<return_statement_node>();
+ n->identifier = &curr();
+ next();
+
+ // If there's no optional return expression, we're done
+ if (curr().type() == lexeme::Semicolon) {
+ next();
+ return n;
+ }
+
+ // Handle the return expression
+ auto x = expression();
+ if (!x) {
+ error("invalid return expression", true, {}, true);
+ return {};
+ }
+ n->expression = std::move(x);
+
+ // Final semicolon
+ if (curr().type() != lexeme::Semicolon) {
+ error("missing ; after return");
+ next();
+ return {};
+ }
+
+ next();
+ return n;
+ }
+
+
+ //G iteration-statement:
+ //G label? 'while' logical-or-expression next-clause? compound-statement
+ //G label? 'do' compound-statement next-clause? 'while' logical-or-expression ';'
+ //G label? 'for' expression next-clause? 'do' unnamed-declaration
+ //G
+ //G label:
+ //G identifier ':'
+ //G
+ //G next-clause:
+ //G 'next' assignment-expression
+ //G
+ auto iteration_statement()
+ -> std::unique_ptr<iteration_statement_node>
+ {
+ auto n = std::make_unique<iteration_statement_node>();
+
+ // If the next three tokens are:
+ // identifier ':' 'for/while/do'
+ // then it's a labeled iteration statement
+ if (
+ curr().type() == lexeme::Identifier
+ && peek(1)
+ && peek(1)->type() == lexeme::Colon
+ && peek(2)
+ && peek(2)->type() == lexeme::Keyword
+ && (*peek(2) == "while" || *peek(2) == "do" || *peek(2) == "for")
+ )
+ {
+ n->label = &curr();
+ next();
+ next();
+ }
+
+ if (
+ curr().type() != lexeme::Keyword
+ || (curr() != "while" && curr() != "do" && curr() != "for")
+ )
+ {
+ return {};
+ }
+
+ n->identifier = &curr();
+ next();
+
+ //-----------------------------------------------------------------
+ // We'll do these same things in different orders,
+ // so extract them into local functions...
+ auto handle_optional_next_clause = [&]() -> bool {
+ if (curr() != "next") {
+ return true; // absent next clause is okay
+ }
+ next(); // don't bother remembering "next" token, shouldn't need its position info
+ auto next = assignment_expression();
+ if (!next) {
+ error("invalid expression after 'next'", true, {}, true);
+ return false;
+ }
+ n->next_expression = std::move(next);
+ return true;
+ };
+
+ auto handle_logical_expression = [&]() -> bool {
+ auto x = logical_or_expression();
+ if (!x) {
+ error("a loop must have a valid conditional expression");
+ return false;
+ }
+ n->condition = std::move(x);
+ return true;
+ };
+
+ auto handle_compound_statement = [&]() -> bool {
+ auto s = compound_statement();
+ if (!s) {
+ error("invalid while loop body", true, {}, true);
+ return false;
+ }
+ n->statements = std::move(s);
+ return true;
+ };
+ //-----------------------------------------------------------------
+
+ // Handle "while"
+ //
+ if (*n->identifier == "while")
+ {
+ if (!handle_logical_expression ()) { return {}; }
+ if (!handle_optional_next_clause()) { return {}; }
+ if (!handle_compound_statement ()) { return {}; }
+ if (!done() && curr().type() == lexeme::Semicolon) {
+ error("a loop body may not be followed by a semicolon (empty statements are not allowed)");
+ return {};
+ }
+ return n;
+ }
+
+ // Handle "do"
+ //
+ else if (*n->identifier == "do")
+ {
+ if (!handle_compound_statement ()) { return {}; }
+ if (!handle_optional_next_clause()) { return {}; }
+ if (curr() != "while") {
+ error("do loop body must be followed by 'while'");
+ return {};
+ }
+ next();
+ if (!handle_logical_expression ()) { return {}; }
+ if (curr().type() != lexeme::Semicolon) {
+ error("missing ; after do..while loop condition");
+ next();
+ return {};
+ }
+ next();
+ return n;
+ }
+
+ // Handle "for"
+ //
+ else if (*n->identifier == "for")
+ {
+ n->range = expression();
+ if (!n->range) {
+ error("expected valid range expression after 'for'", true, {}, true);
+ return {};
+ }
+
+ if (!handle_optional_next_clause()) { return {}; }
+
+ if (
+ curr() != "do"
+ || !peek(1)
+ || peek(1)->type() != lexeme::LeftParen
+ )
+ {
+ 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
+ if (n->parameter->pass == passing_style::in) {
+ n->for_with_in = true;
+ }
+
+ if (!done() && curr().type() == lexeme::Semicolon) {
+ error("a loop body may not be followed by a semicolon (empty statements are not allowed)");
+ return {};
+ }
+
+ return n;
+ }
+
+ assert(!"compiler bug: unexpected case");
+ return {};
+ }
+
+
+ //G alternative:
+ //G alt-name? is-type-constraint '=' statement
+ //G alt-name? is-value-constraint '=' statement
+ //G alt-name? as-type-cast '=' statement
+ //G
+ //GTODO alt-name:
+ //G unqualified-id ':'
+ //G
+ auto alternative()
+ -> std::unique_ptr<alternative_node>
+ {
+ auto n = std::make_unique<alternative_node>();
+
+ if (
+ curr().type() == lexeme::Identifier
+ && peek(1)
+ && peek(1)->type() == lexeme::Colon
+ )
+ {
+ error("(temporary alpha limitation) declaring an identifier is not supported yet");
+ return {};
+ }
+
+ // Now we should be as "is" or "as"
+ // (initial partial implementation, just "is/as id-expression")
+ if (
+ curr() != "is"
+ && curr() != "as"
+ )
+ {
+ return {};
+ }
+
+ n->is_as_keyword = &curr();
+ next();
+
+ if (auto id = type_id()) {
+ n->type_id = std::move(id);
+ }
+ else if (auto e = postfix_expression()) {
+ n->value = std::move(e);
+ }
+ else {
+ error("expected type-id or value after 'is' in inspect alternative", true, {}, true);
+ return {};
+ }
+
+ if (curr().type() != lexeme::Assignment) {
+ error("expected = at start of inspect alternative body", true, {}, true);
+ return {};
+ }
+ n->equal_sign = curr().position();
+ next();
+
+ if (auto s = statement(true, n->equal_sign)) {
+ n->statement = std::move(s);
+ }
+ else {
+ error("expected statement after = in inspect alternative", true, {}, true);
+ return {};
+ }
+
+ return n;
+ }
+
+
+ //G inspect-expression:
+ //G 'inspect' 'constexpr'? expression '{' alternative-seq? '}'
+ //G 'inspect' 'constexpr'? expression '->' type-id '{' alternative-seq? '}'
+ //G
+ //G alternative-seq:
+ //G alternative
+ //G alternative-seq alternative
+ //G
+ auto inspect_expression(bool is_expression)
+ -> std::unique_ptr<inspect_expression_node>
+ {
+ if (curr() != "inspect") {
+ return {};
+ }
+
+ if (!is_expression) {
+ errors.emplace_back(
+ curr().position(),
+ "(temporary alpha limitation) cppfront is still learning 'inspect' - only inspect expressions are currently supported"
+ );
+ return {};
+ }
+
+ auto n = std::make_unique<inspect_expression_node>();
+ n->identifier = &curr();
+ next();
+
+ if (curr() == "constexpr") {
+ n->is_constexpr = true;
+ next();
+ }
+
+ if (auto e = expression(true, false)) {
+ n->expression = std::move(e);
+ }
+ else {
+ error("invalid inspect expression", true, {}, true);
+ return {};
+ }
+
+ // Handle the optional explicit return type
+ if (curr().type() == lexeme::Arrow)
+ {
+ if (!is_expression) {
+ error("an inspect statement cannot have an explicit return type (whereas an inspect expression must have one)");
+ return {};
+ }
+ next();
+ if (curr().type() == lexeme::LeftParen) {
+ error("multiple/named returns are not currently allowed for inspect");
+ return {};
+ }
+
+ auto type = type_id();
+ if (!type) {
+ error("expected a valid inspect return type after ->");
+ return {};
+ }
+ n->result_type = std::move(type);
+ }
+ else if (is_expression) {
+ error("an inspect expression must have an explicit '-> result_type'");
+ return {};
+ }
+
+ // Now do the inspect body
+ if (curr().type() != lexeme::LeftBrace) {
+ error("expected { at start of inspect body");
+ return {};
+ }
+ n->open_brace = curr().position();
+ next();
+
+ while (curr().type() != lexeme::RightBrace)
+ {
+ auto a = alternative();
+ if (!a) {
+ error("invalid alternative in inspect", true, {}, true);
+ return {};
+ }
+ if (
+ is_expression
+ && !a->statement->is_expression()
+ )
+ {
+ error("an inspect expression alternative must be just an expression "
+ "(not a braced block) that will be used as the value of the inspect expression");
+ return {};
+ }
+ n->alternatives.push_back( std::move(a) );
+ }
+
+ n->close_brace = curr().position();
+ next();
+
+ if (n->alternatives.empty()) {
+ error("inspect body cannot be empty - add at least one alternative");
+ return {};
+ }
+
+ return n;
+ }
+
+
+ //G jump-statement:
+ //G 'break' identifier? ';'
+ //G 'continue' identifier? ';'
+ //G
+ auto jump_statement()
+ -> std::unique_ptr<jump_statement_node>
+ {
+ auto n = std::make_unique<jump_statement_node>();
+
+ if (
+ curr() != "break"
+ && curr() != "continue"
+ )
+ {
+ return {};
+ }
+
+ n->keyword = &curr();
+ next();
+
+ if (curr().type() == lexeme::Identifier) {
+ n->label = &curr();
+ next();
+ }
+
+ if (curr().type() != lexeme::Semicolon) {
+ error("expected ; at end of jump-statement");
+ return {};
+ }
+ next();
+
+ return n;
+ }
+
+
+ //G using-statement:
+ //G 'using' id-expression ';'
+ //G 'using' 'namespace' id-expression ';'
+ //G
+ auto using_statement()
+ -> std::unique_ptr<using_statement_node>
+ {
+ auto n = std::make_unique<using_statement_node>();
+
+ if (curr() != "using") {
+ return {};
+ }
+ n->keyword = &curr();
+ next();
+
+ if (curr() == "namespace") {
+ n->for_namespace = true;
+ next();
+ }
+
+ auto id = id_expression();
+ if (!id) {
+ error(std::string{"expected valid id-expression after 'using"} + (n->for_namespace ? " namespace" : "") + "'");
+ return {};
+ }
+
+ n->id = std::move(id);
+
+ if (curr().type() != lexeme::Semicolon) {
+ error("expected ; at end of using-statement");
+ return {};
+ }
+ next();
+
+ return n;
+ }
+
+
+ //G statement:
+ //G selection-statement
+ //G using-statement
+ //G inspect-expression
+ //G return-statement
+ //G jump-statement
+ //G iteration-statement
+ //G compound-statement
+ //G contract-statement
+ //G declaration
+ //G expression-statement
+ //G
+ //G contract-statement
+ //G contract ';'
+ //
+ //GTODO try-block
+ //G
+ auto statement(
+ bool semicolon_required = true,
+ source_position equal_sign = source_position{},
+ bool parameters_allowed = false,
+ compound_statement_node* compound_parent = nullptr
+ )
+ -> std::unique_ptr<statement_node>
+ {
+ if (!done() && curr().type() == lexeme::Semicolon) {
+ error("empty statement is not allowed - remove extra semicolon");
+ return {};
+ }
+
+ auto n = std::make_unique<statement_node>(compound_parent);
+
+ // 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
+ && param->direction() != passing_style::copy
+ )
+ {
+ error("(temporary alpha limitation) parameters scoped to a block/statement must be 'in' (the default), 'copy', or 'inout'", false);
+ return {};
+ }
+ }
+ }
+ }
+
+ // Now handle the rest of the statement
+
+ if (auto s = selection_statement()) {
+ n->statement = std::move(s);
+ assert (n->is_selection());
+ return n;
+ }
+
+ else if (auto s = using_statement()) {
+ n->statement = std::move(s);
+ assert (n->is_using());
+ return n;
+ }
+
+ else if (auto i = inspect_expression(false)) {
+ n->statement = std::move(i);
+ assert (n->is_inspect());
+ return n;
+ }
+
+ else if (auto s = return_statement()) {
+ n->statement = std::move(s);
+ assert (n->is_return());
+ return n;
+ }
+
+ else if (auto s = jump_statement()) {
+ n->statement = std::move(s);
+ assert (n->is_jump());
+ return n;
+ }
+
+ else if (auto s = iteration_statement()) {
+ n->statement = std::move(s);
+ assert (n->is_iteration());
+ return n;
+ }
+
+ else if (auto s = compound_statement(equal_sign)) {
+ n->statement = std::move(s);
+ assert (n->is_compound());
+ return n;
+ }
+
+ else if (auto s = contract()) {
+ if (*s->kind != "assert") {
+ error("only 'assert' contracts are allowed at statement scope");
+ return {};
+ }
+ if (curr().type() != lexeme::Semicolon) {
+ error("missing ';' after contract-statement");
+ return {};
+ }
+ next();
+ n->statement = std::move(s);
+ assert (n->is_contract());
+ return n;
+ }
+
+ else if (auto s = declaration(true, false, false, n.get())) {
+ n->statement = std::move(s);
+ assert (n->is_declaration());
+ return n;
+ }
+
+ else if (auto s = expression_statement(semicolon_required)) {
+ n->statement = std::move(s);
+ assert (n->is_expression());
+ return n;
+ }
+
+ else {
+ return {};
+ }
+ }
+
+
+ //G compound-statement:
+ //G '{' statement-seq? '}'
+ //G
+ //G statement-seq:
+ //G statement
+ //G statement-seq statement
+ //G
+ auto compound_statement(
+ source_position equal_sign = source_position{},
+ bool allow_single_unbraced_statement = false
+ )
+ -> std::unique_ptr<compound_statement_node>
+ {
+ bool is_braced = curr().type() == lexeme::LeftBrace;
+ if (
+ !is_braced
+ && !allow_single_unbraced_statement
+ )
+ {
+ return {};
+ }
+
+ auto n = std::make_unique<compound_statement_node>();
+ if (!is_braced) {
+ n->body_indent = curr().position().colno-1;
+ }
+ else if (peek(1)) {
+ n->body_indent = peek(1)->position().colno-1;
+ }
+
+ // Remember current position, in case this isn't a valid statement
+ auto start_pos = pos;
+
+ // In the case where this is a declaration initializer with
+ // = {
+ // on the same line, we want to remember our start position
+ // as where the = was, not where the { was
+ if (equal_sign.lineno == curr().position().lineno) {
+ n->open_brace = equal_sign;
+ }
+ else {
+ n->open_brace = curr().position();
+ }
+
+ if (is_braced) {
+ next();
+ }
+
+ while (
+ curr().type() != lexeme::RightBrace
+ && (
+ is_braced
+ || std::ssize(n->statements) < 1
+ )
+ )
+ {
+ // Only inside a compound-statement, a
+ // contained statement() may have parameters
+ auto s = statement(true, source_position{}, true, n.get());
+ if (!s) {
+
+ // Only add error when no specific one already exist
+ if(!has_error()) {
+ error("invalid statement encountered inside a compound-statement", true);
+ }
+ pos = start_pos; // backtrack
+ return {};
+ }
+ n->statements.push_back( std::move(s) );
+ }
+
+ if (is_braced) {
+ assert(curr().type() == lexeme::RightBrace);
+ n->close_brace = curr().position();
+ next();
+ }
+ return n;
+ }
+
+
+ //G parameter-declaration:
+ //G this-specifier? parameter-direction? declaration
+ //G
+ //G parameter-direction: one of
+ //G 'in' 'copy' 'inout' 'out' 'move' 'forward'
+ //G
+ //G this-specifier:
+ //G 'implicit'
+ //G 'virtual'
+ //G 'override'
+ //G 'final'
+ //G
+ auto parameter_declaration(
+ bool is_returns = false,
+ bool is_named = true,
+ bool is_template = true,
+ bool is_statement = false
+ )
+ -> std::unique_ptr<parameter_declaration_node>
+ {
+ // Remember current position, because we may need to backtrack if this is just
+ // a parenthesized expression statement, not a statement parameter list
+ auto start_pos = pos;
+
+ auto n = std::make_unique<parameter_declaration_node>();
+ n->pass =
+ is_returns ? passing_style::out :
+ passing_style::in;
+ n->pos = curr().position();
+
+ // Handle optional this-specifier
+ //
+ if (curr() == "implicit") {
+ n->mod = parameter_declaration_node::modifier::implicit;
+ next();
+ }
+ else if (curr() == "virtual") {
+ n->mod = parameter_declaration_node::modifier::virtual_;
+ next();
+ }
+ else if (curr() == "override") {
+ n->mod = parameter_declaration_node::modifier::override_;
+ next();
+ }
+ else if (curr() == "final") {
+ n->mod = parameter_declaration_node::modifier::final_;
+ next();
+ }
+
+ // Handle optional parameter-direction
+ //
+ if (auto dir = to_passing_style(curr());
+ dir != passing_style::invalid
+ )
+ {
+ if (is_template) {
+ error("a template parameter cannot have a passing style (it is always implicitly 'in')");
+ return {};
+ }
+
+ if (is_returns)
+ {
+ if (dir == passing_style::in) {
+ error("a return value cannot be 'in'");
+ return {};
+ }
+ if (dir == passing_style::copy) {
+ error("a return value cannot be 'copy'");
+ return {};
+ }
+ if (dir == passing_style::inout) {
+ error("a return value cannot be 'inout'");
+ return {};
+ }
+ if (dir == passing_style::move) {
+ error("a return value cannot be 'move' (it is implicitly 'move'-out)");
+ return {};
+ }
+ }
+ if (
+ !is_named
+ && dir == passing_style::out
+ )
+ {
+ error("(temporary alpha limitation) an unnamed function cannot have an 'out' parameter");
+ return {};
+ }
+ n->pass = dir;
+ next();
+ }
+
+ // Now the main declaration
+ //
+ if (!(n->declaration = declaration(false, true, is_template))) {
+ pos = start_pos; // backtrack
+ return {};
+ }
+
+ // And some error checks
+ //
+ if (
+ n->mod != parameter_declaration_node::modifier::none
+ && !n->declaration->has_name("this")
+ )
+ {
+ error( "only a 'this' parameter may be declared implicit, virtual, override, or final", false );
+ }
+
+ if (
+ n->declaration->has_name("this")
+ && n->pass != passing_style::in
+ && n->pass != passing_style::inout
+ && n->pass != passing_style::out
+ && n->pass != passing_style::move
+ )
+ {
+ error( "a 'this' parameter must be in, inout, out, or move", false );
+ }
+
+ if (
+ n->declaration->has_name("that")
+ && n->pass != passing_style::in
+ && n->pass != passing_style::move
+ )
+ {
+ error( "a 'that' parameter must be in or move", false );
+ }
+
+ // The only parameter type that could be const-qualified is a 'copy' parameter, because
+ // only it is always truly its own variable, so it makes sense to let the user qualify it;
+ // all the other parameter types are conceptually (usually actually) bound to their args
+ if (
+ !is_returns
+ && n->declaration->is_const()
+ && n->pass != passing_style::copy
+ && n->pass != passing_style::inout
+ )
+ {
+ switch (n->pass) {
+ break;case passing_style::in:
+ error( "an 'in' parameter is always const, 'const' isn't needed and isn't allowed", false );
+ break;case passing_style::inout:
+ // error( "an 'inout' parameter can't be const, if you do want it to be const then use 'in' instead", false );
+ break;case passing_style::out:
+ error( "an 'out' parameter can't be const, otherwise it can't be initialized in the function body", false );
+ break;case passing_style::move:
+ error( "a 'move' parameter can't be const, otherwise it can't be moved from in the function body", false );
+ break;case passing_style::forward:
+ error( "a 'forward' parameter shouldn't be const, because it passes along the argument's actual const-ness (and actual value category)", false );
+ break;default:
+ assert (!"ICE: missing case");
+ }
+ return {};
+ }
+
+ if (
+ !is_returns
+ && !is_statement
+ && n->declaration->initializer
+ )
+ {
+ error("Cpp2 is currently exploring the path of not allowing default arguments - use overloading instead", false);
+ return {};
+ }
+ if (is_named && is_returns) {
+ auto tok = n->name();
+ assert(tok);
+ if (tok->type() != lexeme::Identifier) {
+ error("expected identifier, not '" + tok->to_string() + "'",
+ false, tok->position());
+ }
+ else if (n->declaration->has_wildcard_type()) {
+ error("return parameter '" + tok->to_string() + "' must have a type",
+ false, tok->position());
+ }
+ }
+ return n;
+ }
+
+
+ //G parameter-declaration-list
+ //G '(' parameter-declaration-seq? ')'
+ //G
+ //G parameter-declaration-seq:
+ //G parameter-declaration
+ //G parameter-declaration-seq ',' parameter-declaration
+ //G
+ auto parameter_declaration_list(
+ bool is_returns = false,
+ bool is_named = true,
+ 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) {
+ opener = lexeme::Less;
+ closer = lexeme::Greater;
+ }
+
+ if (curr().type() != opener) {
+ return {};
+ }
+
+ auto n = std::make_unique<parameter_declaration_list_node>();
+ n->open_paren = &curr();
+ next();
+
+ auto param = std::make_unique<parameter_declaration_node>();
+
+ auto count = 1;
+ auto expect_another_param_decl = false;
+
+ while ((param = parameter_declaration(is_returns, is_named, is_template, is_statement)) != nullptr)
+ {
+ expect_another_param_decl = false;
+ param->ordinal = count;
+ ++count;
+
+ if (
+ std::ssize(n->parameters) > 1
+ && n->parameters.back()->has_name("that")
+ )
+ {
+ error("'that' may not be followed by any additional parameters", false);
+ }
+
+ n->parameters.push_back( std::move(param) );
+
+ if (curr().type() == closer) {
+ break;
+ }
+ else if (curr().type() != lexeme::Comma) {
+ if (is_statement) {
+ pos = start_pos; // backtrack
+ }
+ else {
+ error("expected ',' in parameter list", true, {}, true);
+ }
+ return {};
+ }
+
+ expect_another_param_decl = true;
+ next();
+ }
+
+ if (expect_another_param_decl) {
+ error("invalid parameter list: a comma must be followed by another parameter", true, {}, true);
+ }
+
+ if (curr().type() != closer) {
+ if (is_statement) {
+ pos = start_pos; // backtrack
+ }
+ else {
+ error("invalid parameter list", true, {}, true);
+ }
+ return {};
+ }
+
+ n->close_paren = &curr();
+ next();
+ return n;
+ }
+
+
+ //G contract:
+ //G contract-kind contract-group? ':' '(' logical-or-expression ')'
+ //G contract-kind contract-group? ':' '(' logical-or-expression ',' expression ')'
+ //G
+ //G contract-group:
+ //G '<' id-expression contract-flags?'>'
+ //G
+ //G contract-flags:
+ //G ',' id-expression contract-flags?
+ //G
+ //G contract-kind: one of
+ //G 'pre' 'post' 'assert'
+ //G
+ auto contract()
+ -> std::unique_ptr<contract_node>
+ {
+ auto n = std::make_unique<contract_node>(curr().position());
+ auto guard = capture_groups_stack_guard(this, &n->captures);
+
+ if (
+ curr() != "pre"
+ && curr() != "post"
+ && curr() != "assert"
+ )
+ {
+ return {};
+ }
+ n->kind = &curr();
+ next();
+
+ // Check if there's a <group,flags>
+ if (curr().type() == lexeme::Less) {
+ next();
+ if (auto id = id_expression()) {
+ n->group = std::move(id);
+ }
+ else {
+ error("invalid contract group after '<'");
+ return {};
+ }
+
+ // Now check if there's a list of flags
+ while (curr().type() == lexeme::Comma) {
+ next();
+ if (auto id = id_expression()) {
+ n->flags.push_back( std::move(id) );
+ }
+ else {
+ error("invalid contract tag in list");
+ return {};
+ }
+ }
+
+ if (curr().type() != lexeme::Greater) {
+ error("expected '>' after contract group");
+ return {};
+ }
+ next();
+ }
+
+ if (curr().type() != lexeme::LeftParen) {
+ error("expected '(' before the contract condition");
+ return {};
+ }
+ next();
+
+ auto condition = logical_or_expression();
+ if (!condition) {
+ error("invalid contract condition", true, {}, true);
+ return {};
+ }
+ n->condition = std::move(condition);
+
+ // Now check for the optional string message
+ if (curr().type() == lexeme::Comma) {
+ next();
+ n->message = expression();
+ if (!n->message) {
+ error("a contract violation message must be a valid string expression", true, {}, true);
+ return {};
+ }
+ }
+
+ if (curr().type() != lexeme::RightParen) {
+ error("expected ')' at the end of the contract");
+ return {};
+ }
+ next();
+
+ return n;
+ }
+
+
+ //G function-type:
+ //G parameter-declaration-list throws-specifier? return-list? contract-seq?
+ //G
+ //G throws-specifier:
+ //G 'throws'
+ //G
+ //G return-list:
+ //G expression-statement
+ //G '->' parameter-direction? type-id
+ //G '->' parameter-declaration-list
+ //G
+ //G contract-seq:
+ //G contract
+ //G contract-seq contract
+ //G
+ auto function_type(
+ declaration_node* my_decl,
+ bool is_named = true
+ )
+ -> std::unique_ptr<function_type_node>
+ {
+ auto n = std::make_unique<function_type_node>( my_decl );
+
+ // Parameters
+ auto parameters = parameter_declaration_list(false, is_named, false);
+ if (!parameters) {
+ return {};
+ }
+ n->parameters = std::move(parameters);
+
+ // Optional "throws"
+ if (
+ curr().type() == lexeme::Keyword
+ && curr() == "throws"
+ )
+ {
+ if (
+ n->is_move()
+ || n->is_swap()
+ || n->is_destructor()
+ )
+ {
+ error( "(experimental restriction) Cpp2 currently does not allow a move, swap, or destructor function to be designated 'throws'" );
+ return {};
+ }
+
+ n->throws = true;
+ next();
+ }
+
+
+ // If we're not at a '->' or 'requires' or contract and what follows is
+ // an expression, this is a ":(params) expr" shorthand function syntax
+ if (
+ curr().type() != lexeme::Arrow
+ && curr() != "requires"
+ && (curr() != "pre" && curr() != "post")
+ )
+ {
+ auto start_pos = pos;
+ auto at_an_expression = expression() != nullptr;
+ pos = start_pos; // backtrack no matter what, we're just peeking here
+
+ if (at_an_expression) {
+ n->returns = function_type_node::single_type_id{ std::make_unique<type_id_node>() };
+ assert(n->returns.index() == function_type_node::id);
+ n->my_decl->terse_no_equals = true;
+ return n;
+ }
+ }
+
+
+ // Optional returns
+ if (curr().type() == lexeme::Arrow)
+ {
+ next();
+
+ if (auto pass = to_passing_style(curr());
+ pass != passing_style::invalid
+ )
+ {
+ if (
+ pass != passing_style::forward
+ && pass != passing_style::move
+ )
+ {
+ error("only 'forward' and 'move' return passing style are allowed from functions");
+ }
+ next();
+ if (auto t = type_id()) {
+ n->returns = function_type_node::single_type_id{ std::move(t), pass };
+ assert(n->returns.index() == function_type_node::id);
+ }
+ else {
+ auto msg = std::string("'");
+ msg += to_string_view(pass);
+ error(msg + "' must be followed by a type-id");
+ }
+ }
+
+ else if (auto t = type_id())
+ {
+ if (
+ t->get_token()
+ && t->get_token()->to_string() == "auto"
+ )
+ {
+ auto name = std::string{"v"};
+ if (my_decl && my_decl->name()) {
+ name = my_decl->name()->to_string();
+ }
+ errors.emplace_back(
+ curr().position(),
+ "to define a function " + name + " with deduced return type, write '" + name + ": ( /* arguments */ ) -> _ = { /* function body */ }'"
+ );
+ return {};
+ }
+ n->returns = function_type_node::single_type_id{ std::move(t), passing_style::move };
+ assert(n->returns.index() == function_type_node::id);
+ }
+
+ else if (auto returns_list = parameter_declaration_list(true, is_named))
+ {
+ if (std::ssize(returns_list->parameters) < 1) {
+ error("an explicit return value list cannot be empty");
+ return {};
+ }
+ n->returns = std::move(returns_list);
+ assert(n->returns.index() == function_type_node::list);
+ }
+
+ else
+ {
+ error("missing function return after ->");
+ return {};
+ }
+ }
+
+ // Pre/post conditions
+ while (auto c = contract())
+ {
+ if (
+ *c->kind != "pre"
+ && *c->kind != "post"
+ )
+ {
+ error("only 'pre' and 'post' contracts are allowed on functions");
+ return {};
+ }
+ n->contracts.push_back( std::move(c) );
+ }
+
+ return n;
+ }
+
+
+ auto apply_type_metafunctions( declaration_node& decl )
+ -> bool;
+
+
+ //G unnamed-declaration:
+ //G ':' meta-functions-list? template-parameter-declaration-list? function-type requires-clause? '=' statement
+ //G ':' meta-functions-list? template-parameter-declaration-list? function-type statement
+ //G ':' meta-functions-list? template-parameter-declaration-list? type-id? requires-clause? '=' statement
+ //G ':' meta-functions-list? template-parameter-declaration-list? type-id
+ //G ':' meta-functions-list? template-parameter-declaration-list? 'final'? 'type' requires-clause? '=' statement
+ //G ':' 'namespace' '=' statement
+ //G
+ //G meta-functions-list:
+ //G '@' id-expression
+ //G meta-functions-list '@' id-expression
+ //G
+ //G requires-clause:
+ //G # note: for aliases, == is not allowed in expressions until new ( is opened
+ //G 'requires' logical-or-expression
+ //G
+ //G template-parameter-declaration-list
+ //G '<' parameter-declaration-seq '>'
+ //G
+ auto unnamed_declaration(
+ source_position start,
+ bool semicolon_required = true,
+ bool captures_allowed = false,
+ bool named = false,
+ bool is_parameter = false,
+ bool is_template_parameter = false,
+ std::unique_ptr<unqualified_id_node> id = {},
+ accessibility access = {},
+ bool is_variadic = false,
+ statement_node* my_stmt = {}
+ )
+ -> std::unique_ptr<declaration_node>
+ {
+ auto n = std::make_unique<declaration_node>( current_declarations.back() );
+ n->pos = start;
+
+ n->identifier = std::move(id);
+ n->access = access;
+ n->is_variadic = is_variadic;
+ n->my_statement = my_stmt;
+
+ // If we're in a type scope and the next token is ';', treat this as if
+ // ': _;' without an initializer.
+ // This is for type metafunctions that want to use the incomplete name-only
+ // declaration, and transform it to something else. If unchanged the
+ // incomplete declaration will be rejected later by sema.check rule.
+ if (
+ n->parent_is_type()
+ && curr().type() == lexeme::Semicolon
+ )
+ {
+ n->type = std::make_unique<type_id_node>();
+ assert (n->is_object());
+ next();
+ return n;
+ }
+
+ // For a template parameter, ':' is not required and
+ // we default to ': type'
+ if (
+ is_template_parameter
+ && curr().type() != lexeme::Colon
+ )
+ {
+ // So invent the "type" token
+ generated_text.push_back("type");
+ generated_tokens->push_back({
+ generated_text.back().c_str(),
+ std::ssize(generated_text.back()),
+ start,
+ lexeme::Identifier
+ });
+
+ // So we can create the type_node
+
+ auto t = std::make_unique<type_node>( &generated_tokens->back() );
+
+ n->type = std::move(t);
+ assert (n->is_type());
+
+ // That's it, we're done here
+ return n;
+ }
+
+ // For 'this' and 'that' parameters ':' is not allowed and we'll use the default ': _'
+ if (
+ n->identifier
+ && is_parameter
+ && (
+ *n->identifier->identifier == "this"
+ || *n->identifier->identifier == "that"
+ )
+ && curr().type() == lexeme::Colon
+ )
+ {
+ error("a 'this' or 'that' parameter knows its type, no ':' is allowed here", false);
+ return {};
+ }
+
+ // For an ordinary parameter, ':' is not required and
+ // we default to ': _' - i.e., deduced with no initializer
+ if (
+ is_parameter
+ && curr().type() != lexeme::Colon
+ )
+ {
+ // So invent the "_" token
+ generated_text.push_back("_");
+ generated_tokens->push_back({
+ generated_text.back().c_str(),
+ std::ssize(generated_text.back()),
+ start,
+ lexeme::Identifier
+ });
+
+ // So we can create the typeid_id_node and its unqualified_id_node
+
+ auto gen_id = std::make_unique<unqualified_id_node>();
+ gen_id->identifier = &generated_tokens->back();
+
+ auto type = std::make_unique<type_id_node>();
+ type->pos = start;
+ type->id = std::move(gen_id);
+
+ n->type = std::move(type);
+ assert (n->is_object());
+
+ // That's it, we're done here
+ return n;
+ }
+
+ // Otherwise, the next token must be ':'
+ if (curr().type() != lexeme::Colon) {
+ return {};
+ }
+ next();
+
+ if (curr() == "union") {
+ error("unsafe 'union' is not supported in Cpp2 - write '@union' to apply Cpp2's safe 'union' type metafunction instead, or use std::variant");
+ }
+
+ // Next is an optional metafunctions clause
+ while (curr() == "@") {
+ next();
+ auto idx = id_expression();
+ if (!idx) {
+ error("'@' must be followed by a metafunction name", false);
+ return {};
+ }
+ n->metafunctions.push_back( std::move(idx) );
+ }
+
+ // Next is an optional template parameter list
+ if (curr().type() == lexeme::Less) {
+ auto template_parameters = parameter_declaration_list(false, false, true);
+ if (!template_parameters) {
+ error("invalid template parameter list");
+ return {};
+ }
+ n->template_parameters = std::move(template_parameters);
+ }
+
+ auto guard =
+ captures_allowed
+ ? std::make_unique<capture_groups_stack_guard>(this, &n->captures)
+ : std::unique_ptr<capture_groups_stack_guard>()
+ ;
+
+ auto guard2 = current_declarations_stack_guard(this, n.get());
+
+ // Next is an an optional type
+
+ auto deduced_type = false;
+
+ // It could be "type", declaring a user-defined type
+ if (
+ curr() == "type"
+ || (
+ curr() == "final"
+ && peek(1) && *peek(1) == "type"
+ )
+ )
+ {
+ n->type = std::make_unique<type_node>( &curr(), curr() == "final" );
+
+ if (curr() == "final") {
+ next();
+ }
+ next();
+
+ if (
+ is_parameter
+ && !is_template_parameter
+ )
+ {
+ error("a normal parameter cannot be a 'type' - did you mean to put this in a < > template parameter list?");
+ return {};
+ }
+ assert (n->is_type());
+ }
+
+ // Or a function type, declaring a function - and tell the function whether it's in a user-defined type
+ else if (auto t = function_type(n.get(), named))
+ {
+ n->type = std::move(t);
+ assert (n->is_function());
+
+ if (!n->metafunctions.empty()) {
+ errors.emplace_back(
+ n->metafunctions.front()->position(),
+ "(temporary alpha limitation) metafunctions are currently not supported on functions, only on types"
+ );
+ return {};
+ }
+ }
+
+ // Or a namespace
+ else if (curr() == "namespace")
+ {
+ n->type = std::make_unique<namespace_node>( &curr() );
+ assert (n->type.index() == declaration_node::a_namespace);
+ next();
+
+ if (!n->metafunctions.empty()) {
+ errors.emplace_back(
+ n->metafunctions.front()->position(),
+ "(temporary alpha limitation) metafunctions are currently not supported on namespaces, only on types"
+ );
+ return {};
+ }
+ }
+
+ // Or just a type-id, declaring a non-pointer object
+ else if (auto t = type_id())
+ {
+ if (
+ t->get_token()
+ && t->get_token()->to_string() == "auto"
+ )
+ {
+ auto name = std::string{"v"};
+ if (n->name()) {
+ name = n->name()->to_string();
+ }
+ errors.emplace_back(
+ curr().position(),
+ "to define a variable " + name + " with deduced type, write '" + name + " := /* initializer */;'"
+ );
+ return {};
+ }
+
+ n->type = std::move(t);
+ assert (n->is_object());
+
+ if (!n->metafunctions.empty()) {
+ errors.emplace_back(
+ n->metafunctions.front()->position(),
+ "(temporary alpha limitation) metafunctions are currently not supported on objects, only on types"
+ );
+ return {};
+ }
+
+ if (curr().type() == lexeme::LeftBracket) {
+ error("C-style array types are not allowed, use std::array instead");
+ return {};
+ }
+ }
+
+ // Or nothing, declaring an object of deduced type,
+ // which we'll represent using an empty type-id
+ else {
+ n->type = std::make_unique<type_id_node>();
+ assert (n->is_object());
+ deduced_type = true;
+ }
+
+ // If we've already validated that this is a function where the parameter
+ // list is followed by a valid expression-statement, parse that again
+ // (requiring a semicolon as we validated when determining terse_no_equals)
+ if (n->terse_no_equals)
+ {
+ n->equal_sign = curr().position();
+ n->initializer = statement(/*ignore semicolon_required*/ false, n->equal_sign);
+ assert( n->initializer && "ICE: should have already validated that there's a valid expression-statement here" );
+ }
+
+ else
+ {
+ // Next is optionally a requires clause (if not using the "-> expr;" syntax)
+ if (curr() == "requires")
+ {
+ if (
+ n->is_type()
+ && !n->template_parameters
+ )
+ {
+ error("'requires' is not allowed on a type that does not have a template parameter list");
+ return {};
+ }
+
+ if (n->is_namespace())
+ {
+ error("'requires' is not allowed on a namespace");
+ return {};
+ }
+
+ n->requires_pos = curr().position();
+ next();
+ auto e = logical_or_expression();
+ if (!e) {
+ error("'requires' must be followed by an expression");
+ return {};
+ }
+ n->requires_clause_expression = std::move(e);
+ }
+
+ // Next is optionally = or == followed by an initializer
+
+ // If there is no = or ==
+ if (
+ !done()
+ && curr().type() != lexeme::Assignment
+ && curr().type() != lexeme::EqualComparison
+ )
+ {
+ if (
+ n->is_type()
+ && !is_template_parameter
+ )
+ {
+ error("a user-defined type must have an = initializer");
+ return {};
+ }
+
+ // Then there may be a semicolon
+ // If there is a semicolon, eat it
+ if (!done() && curr().type() == lexeme::Semicolon) {
+ next();
+ }
+ // But if there isn't one and it was required, diagnose an error
+ else if (semicolon_required) {
+ if (curr().type() == lexeme::LeftBrace) {
+ error("expected '=' before '{' - did you mean '= {' ?", true, {}, true);
+ }
+ else {
+ error("missing ';' at end of declaration or '=' at start of initializer", true, {}, true);
+ }
+ return {};
+ }
+ }
+
+ // There was an = or ==, so eat it and continue
+ else
+ {
+ n->equal_sign = curr().position();
+
+ if (curr().type() == lexeme::EqualComparison) {
+ if (!n->is_function()) {
+ error("syntax error at '==' - did you mean '='?");
+ }
+ n->is_constexpr = true;
+ }
+
+ next();
+
+ if (auto t = std::get_if<declaration_node::an_object>(&n->type);
+ t
+ && (*t)->is_pointer_qualified()
+ )
+ {
+ if (
+ curr() == "nullptr"
+ || isdigit(std::string_view(curr())[0])
+ || (
+ curr() == "("
+ && peek(1)
+ && *peek(1) == ")"
+ )
+ )
+ {
+ error("pointer cannot be initialized to null or int - leave it uninitialized and then set it to a non-null value when you have one");
+ violates_lifetime_safety = true;
+ throw std::runtime_error("null initialization detected");
+ }
+ }
+
+ // deduced_type == true means that the type will be deduced,
+ // represented using an empty type-id
+ if (
+ deduced_type
+ && peek(1)
+ )
+ {
+ auto& type = std::get<declaration_node::an_object>(n->type);
+ // object initialized by the address of the curr() object
+ if (peek(1)->type() == lexeme::Ampersand) {
+ type->address_of = &curr();
+ }
+ // object initialized by (potentially multiple) dereference of the curr() object
+ else if (peek(1)->type() == lexeme::Multiply) {
+ type->dereference_of = &curr();
+ for (int i = 1; peek(i)->type() == lexeme::Multiply; ++i)
+ type->dereference_cnt += 1;
+ }
+ else if (
+ // object initialized by the result of the function call (and it is not unnamed function)
+ (peek(1)->type() == lexeme::LeftParen && curr().type() != lexeme::Colon)
+ || curr().type() == lexeme::Identifier // or by the object (variable that the type need to be checked)
+ ) {
+ type->suspicious_initialization = &curr();
+ }
+ }
+
+ if (!(n->initializer = statement(semicolon_required, n->equal_sign))) {
+ error(
+ "ill-formed initializer",
+ true, {}, true
+ );
+ next();
+ return {};
+ }
+ }
+
+ }
+
+ // A type initializer must be a compound expression
+ if (
+ n->is_type()
+ && !is_parameter
+ && (
+ !n->initializer
+ || !n->initializer->is_compound()
+ )
+ )
+ {
+ errors.emplace_back(
+ n->position(),
+ "a user-defined type initializer must be a compound-expression consisting of declarations"
+ );
+ return {};
+ }
+
+ // If this is a type with metafunctions, apply those
+ if (n->is_type()) {
+ if (!apply_type_metafunctions(*n)) {
+ error(
+ "error encountered while applying type metafunctions",
+ false, {}, true
+ );
+ return {};
+ }
+ }
+
+ if (
+ n->is_function()
+ && n->initializer
+ && !done() && curr().type() == lexeme::Semicolon
+ )
+ {
+ if (n->initializer->is_compound() && n->has_name()) {
+ error("a braced function body may not be followed by a semicolon (empty statements are not allowed)");
+ return {};
+ } else if (n->initializer->is_expression()) {
+ error("a single-expression function should end with a single semicolon");
+ return {};
+ }
+ }
+
+ // If this is a function with a list of multiple/named return values,
+ // and the function body's end doesn't already have "return" as the
+ // last statement, then generate "return;" as the last statement
+ if (auto func = std::get_if<declaration_node::a_function>(&n->type);
+ func
+ && n->initializer
+ && (*func)->returns.index() == function_type_node::list
+ )
+ {
+ if (!n->initializer->is_compound()) {
+ error(
+ "a function with named return value(s) must have a full { } body",
+ false,
+ {},
+ true
+ );
+ return {};
+ }
+
+ auto& body = std::get<statement_node::compound>(n->initializer->statement);
+
+ if (
+ body->statements.empty()
+ || !body->statements.back()->is_return()
+ )
+ {
+ auto last_pos = n->position();
+ if (!body->statements.empty()) {
+ last_pos = body->statements.back()->position();
+ }
+ ++last_pos.lineno;
+ generated_tokens->emplace_back( "return", last_pos, lexeme::Keyword);
+
+ auto ret = std::make_unique<return_statement_node>();
+ ret->identifier = &generated_tokens->back();
+
+ auto stmt = std::make_unique<statement_node>();
+ stmt->statement = std::move(ret);
+
+ body->statements.push_back(std::move(stmt));
+ }
+ }
+
+ // If this is a function, record its extents
+ if (n->is_function()) {
+ function_body_extents.emplace_back(
+ n->equal_sign.lineno,
+ peek(-1)->position().lineno
+ );
+ }
+
+ return n;
+ }
+
+
+ //G alias:
+ //G ':' template-parameter-declaration-list? 'type' requires-clause? '==' type-id ';'
+ //G ':' 'namespace' '==' id-expression ';'
+ //G ':' template-parameter-declaration-list? type-id? requires-clause? '==' expression ';'
+ //G
+ //GT ':' function-type '==' expression ';'
+ //GT # See commit 63efa6ed21c4d4f4f136a7a73e9f6b2c110c81d7 comment
+ //GT # for why I don't see a need to enable this yet
+ //
+ auto alias()
+ -> std::unique_ptr<declaration_node>
+ {
+ // Remember current position, because we need to look ahead
+ auto start_pos = pos;
+
+ auto n = std::make_unique<declaration_node>( current_declarations.back() );
+
+ if (curr().type() != lexeme::Colon) {
+ return {};
+ }
+ next();
+
+ // Next is an optional template parameter list
+ if (curr().type() == lexeme::Less) {
+ auto template_parameters = parameter_declaration_list(false, false, true);
+ if (!template_parameters) {
+ pos = start_pos; // backtrack
+ return {};
+ }
+ n->template_parameters = std::move(template_parameters);
+ }
+
+ auto a = std::make_unique<alias_node>( &curr() );
+
+ // Next must be 'type', 'namespace', a type-id, or we're at the 'requires' or '=='
+ if (curr() == "type")
+ {
+ next();
+ }
+ else if (curr() == "namespace")
+ {
+ next();
+ if (n->template_parameters) {
+ errors.emplace_back(
+ curr().position(),
+ "a namespace or namespace alias cannot have template parameters"
+ );
+ return {};
+ }
+ }
+ else if (curr().type() != lexeme::EqualComparison && curr() != "requires")
+ {
+ a->type_id = type_id();
+ if (!a->type_id) {
+ pos = start_pos; // backtrack
+ return {};
+ }
+ }
+
+ // Next is optionally a requires clause
+ if (curr() == "requires")
+ {
+ if (
+ n->is_type_alias()
+ && !n->template_parameters
+ )
+ {
+ error("'requires' is not allowed on a type alias that does not have a template parameter list");
+ return {};
+ }
+
+ if (n->is_namespace_alias())
+ {
+ error("'requires' is not allowed on a namespace alias");
+ return {};
+ }
+
+ n->requires_pos = curr().position();
+ next();
+ auto e = logical_or_expression(true, false);
+ if (!e) {
+ error("'requires' must be followed by an expression");
+ return {};
+ }
+ n->requires_clause_expression = std::move(e);
+ }
+
+ // Now we should be at the '==' if this is an alias
+
+ if (curr().type() == lexeme::EqualComparison) {
+ next();
+ }
+ else {
+ if (a->type->type() != lexeme::EqualComparison) {
+ pos = start_pos; // backtrack
+ return {};
+ }
+ }
+ assert(peek(-1)->type() == lexeme::EqualComparison);
+
+ if (
+ n->parent_is_type()
+ && *a->type == "namespace"
+ )
+ {
+ errors.emplace_back(
+ curr().position(),
+ "a namespace alias cannot appear in a type scope"
+ );
+ return {};
+ }
+
+ // Finally, pick up the initializer
+
+ // Type alias
+ if (*a->type == "type")
+ {
+ auto t = type_id();
+ if (!t) {
+ errors.emplace_back(
+ curr().position(),
+ "a 'type ==' alias declaration must be followed by a type name"
+ );
+ return {};
+ }
+ if (
+ t->is_wildcard()
+ || ( t->get_token() && t->get_token()->to_string() == "auto" )
+ ) {
+ errors.emplace_back(
+ curr().position(),
+ "a 'type ==' alias declaration must be followed by a type name (not a wildcard _ nor auto)"
+ );
+ return {};
+ }
+ a->initializer = std::move(t);
+ }
+
+ // Namespace alias
+ else if (*a->type == "namespace")
+ {
+ if (auto qid = id_expression()) {
+ a->initializer = std::move(qid);
+ }
+ else {
+ errors.emplace_back(
+ curr().position(),
+ "a 'namespace ==' alias declaration must be followed by a namespace name (id-expression)"
+ );
+ return {};
+ }
+ }
+
+ // Object alias
+ else if (
+ a->type_id
+ || a->type->type() == lexeme::EqualComparison
+ )
+ {
+ auto e = expression();
+ if (!e) {
+ errors.emplace_back(
+ curr().position(),
+ "an object '==' alias declaration must be followed by an expression"
+ );
+ return {};
+ }
+ a->initializer = std::move(e);
+ }
+
+ // Anything else shouldn't be possible
+ else {
+ assert(!"ICE: should be unreachable - invalid alias declaration");
+ return {};
+ }
+
+ // And the final ceremonial semicolon
+ if (curr() != ";") {
+ errors.emplace_back(
+ curr().position(),
+ "';' expected at end of alias declaration"
+ );
+ return {};
+ }
+ next();
+
+ n->type = std::move(a);
+
+ return n;
+ }
+
+
+ //G declaration:
+ //G access-specifier? identifier '...'? unnamed-declaration
+ //G access-specifier? identifier alias
+ //G
+ //G access-specifier:
+ //G public
+ //G protected
+ //G private
+ //G
+ auto declaration(
+ bool semicolon_required = true,
+ bool is_parameter = false,
+ bool is_template_parameter = false,
+ statement_node* my_stmt = {}
+ )
+ -> std::unique_ptr<declaration_node>
+ {
+ if (done()) { return {}; }
+
+ // Remember current position, because we need to look ahead
+ auto start_pos = pos;
+
+ auto n = std::unique_ptr<declaration_node>{};
+
+ // This scope is to ensure that once we've moved 'id' into the
+ // declaration_node, we don't access the moved-from local name
+ // (and similar hygiene for 'access' though that one doesn't matter as much)
+ // The reason to move 'id' into unnamed_declaration() is so that
+ // it can conveniently perform some checks that refer to the name
+ {
+ auto access = accessibility::default_;
+ if (curr() == "public") {
+ access = accessibility::public_;
+ next();
+ }
+ else if (curr() == "protected") {
+ access = accessibility::protected_;
+ next();
+ }
+ else if (curr() == "private") {
+ access = accessibility::private_;
+ next();
+ }
+
+ // If they wrote an access-specifier, see if they put a ':'
+ // after it out of Cpp1 habit (there's no colon in Cpp2)
+ if (
+ access != accessibility::default_
+ && curr().type() == lexeme::Colon
+ )
+ {
+ errors.emplace_back(
+ curr().position(),
+ "':' is not allowed after an access-specifier"
+ );
+ return {};
+ }
+
+ auto id = unqualified_id();
+ if (!id) {
+ return {};
+ }
+
+ if (id->to_string() == "...") {
+ errors.emplace_back(
+ curr().position(),
+ "a variadic declaration must have a name - did you forget to write a name before '...'?"
+ );
+ pos = start_pos; // backtrack
+ }
+
+ auto is_variadic = false;
+ if (curr().type() == lexeme::Ellipsis) {
+ is_variadic = true;
+ next();
+ }
+
+ // Provide some useful Cpp1->Cpp2 migration diagnostics for common mistakes
+ //
+ if (
+ id->get_token()
+ && *id->get_token() == "auto"
+ && curr().type() != lexeme::Colon
+ )
+ {
+ auto name = std::string{"v"};
+ if (peek(0) && peek(0)->type() == lexeme::Identifier) {
+ name = peek(0)->to_string();
+ }
+ errors.emplace_back(
+ curr().position(),
+ "to define a variable " + name + " of type T, write '" + name + ": T = /* initializer */'"
+ );
+ return {};
+ }
+ if (
+ id->get_token()
+ && *id->get_token() == "namespace"
+ && curr().type() != lexeme::Colon
+ )
+ {
+ auto name = std::string{"N"};
+ if (peek(0)) {
+ name = peek(0)->to_string();
+ }
+ errors.emplace_back(
+ curr().position(),
+ "to define a namespace " + name + ", write '" + name + " : namespace = { /*contents*/ }'"
+ );
+ return {};
+ }
+ if (
+ id->get_token()
+ && (
+ *id->get_token() == "class"
+ || *id->get_token() == "struct"
+ )
+ && curr().type() != lexeme::Colon
+ )
+ {
+ auto name = std::string{"C"};
+ if (peek(0)) {
+ name = peek(0)->to_string();
+ }
+ errors.emplace_back(
+ curr().position(),
+ "to define a type " + name + ", write '" + name + " : type = { /*body*/ }'"
+ );
+ return {};
+ }
+
+ // Now proceed...
+ //
+
+ // First see if it's an alias declaration
+ n = alias();
+ if (n) {
+ if (is_parameter) {
+ errors.emplace_back(
+ curr().position(),
+ "a parameter declaration may not be an alias declaration"
+ );
+ return {};
+ }
+
+ if (is_variadic) {
+ errors.emplace_back(
+ curr().position(),
+ "an alias declaration may not be variadic"
+ );
+ return {};
+ }
+
+ n->pos = start_pos;
+ n->identifier = std::move(id);
+ n->access = access;
+ return n;
+ }
+
+ // Otherwise, this is a normal declaration
+ n = unnamed_declaration(
+ start_pos,
+ semicolon_required,
+ false,
+ true,
+ is_parameter,
+ is_template_parameter,
+ std::move(id),
+ access,
+ is_variadic,
+ my_stmt
+ );
+ if (!n) {
+ pos = start_pos; // backtrack
+ return {};
+ }
+ }
+
+ // Note: Do this after trying to parse this as a declaration, for parse backtracking
+
+ if (
+ *n->identifier->identifier == "that"
+ && (
+ !is_parameter
+ || is_template_parameter
+ )
+ )
+ {
+ errors.emplace_back(
+ n->identifier->position(),
+ "'that' may only be declared as an ordinary function parameter"
+ );
+ return {};
+ }
+
+ // Cache some context
+ n->is_template_parameter = is_template_parameter;
+ n->is_parameter = is_parameter;
+
+ return n;
+ }
+
+
+ //G declaration-seq:
+ //G declaration
+ //G declaration-seq declaration
+ //G
+ //G translation-unit:
+ //G declaration-seq?
+ //
+ auto translation_unit()
+ -> std::unique_ptr<translation_unit_node>
+ {
+ auto n = std::make_unique<translation_unit_node>();
+ for (auto d = declaration(); d; d = declaration()) {
+ n->declarations.push_back( std::move(d) );
+ }
+ return n;
+ }
+
+public:
+ //-----------------------------------------------------------------------
+ // debug_print
+ //
+ auto debug_print(std::ostream& o)
+ -> void;
+};
+
+
+//-----------------------------------------------------------------------
+//
+// Common parts for printing visitors
+//
+//-----------------------------------------------------------------------
+//
+struct printing_visitor
+{
+ //-----------------------------------------------------------------------
+ // Constructor: remember a stream to write to
+ //
+ std::ostream& o;
+
+ printing_visitor(std::ostream& out) : o{out} { indent_spaces = 2; }
+};
+
+
+//-----------------------------------------------------------------------
+//
+// Visitor for printing a parse tree
+//
+//-----------------------------------------------------------------------
+//
+class parse_tree_printer : printing_visitor
+{
+ using printing_visitor::printing_visitor;
+
+public:
+ auto start(token const& n, int indent) -> void
+ {
+ o << pre(indent) << _as<std::string>(n.type()) << ": " << n.to_string() << "\n";
+ }
+
+ auto start(literal_node const&, int indent) -> void
+ {
+ o << pre(indent) << "literal" << "\n";
+ }
+
+ auto start(expression_node const& n, int indent) -> void
+ {
+ o << pre(indent) << "expression - "
+ << n.num_subexpressions << " subexpressions, my_statement ["
+ << static_cast<void const*>(n.my_statement) << "]\n";
+ }
+
+ auto start(expression_list_node::term const&n, int indent) -> void
+ {
+ o << pre(indent) << "expression-list term\n";
+ if (n.pass == passing_style::out) {
+ o << pre(indent+1) << "out\n";
+ }
+ }
+
+ auto start(expression_list_node const&, int indent) -> void
+ {
+ o << pre(indent) << "expression-list\n";
+ }
+
+ auto start(primary_expression_node const&, int indent) -> void
+ {
+ o << pre(indent) << "primary-expression\n";
+ }
+
+ auto start(prefix_expression_node const&, int indent) -> void
+ {
+ o << pre(indent) << "prefix-expression\n";
+ }
+
+ auto start(is_as_expression_node const&, int indent) -> void
+ {
+ o << pre(indent) << "is-as-expression\n";
+ }
+
+ template<String Name, typename Term>
+ auto start(binary_expression_node<Name, Term> const&, int indent) -> void
+ {
+ o << pre(indent) << Name.value << "-expression\n";
+ }
+
+ auto start(expression_statement_node const& n, int indent) -> void
+ {
+ o << pre(indent) << "expression-statement - [" << static_cast<void const*>(&n) << "]\n";
+ }
+
+ auto start(postfix_expression_node const&, int indent) -> void
+ {
+ o << pre(indent) << "postfix-expression\n";
+ }
+
+ auto start(unqualified_id_node const&, int indent) -> void
+ {
+ o << pre(indent) << "unqualified-id\n";
+ }
+
+ auto start(qualified_id_node const&, int indent) -> void
+ {
+ o << pre(indent) << "qualified-id\n";
+ }
+
+ auto start(type_id_node const&, int indent) -> void
+ {
+ o << pre(indent) << "type-id\n";
+ }
+
+ auto start(id_expression_node const&, int indent) -> void
+ {
+ o << pre(indent) << "id-expression\n";
+ }
+
+ auto start(statement_node const&, int indent) -> void
+ {
+ o << pre(indent) << "statement\n";
+ }
+
+ auto start(compound_statement_node const&, int indent) -> void
+ {
+ o << pre(indent) << "compound-statement\n";
+ }
+
+ auto start(selection_statement_node const& n, int indent) -> void
+ {
+ o << pre(indent) << "selection-statement\n";
+ o << pre(indent+1) << "is_constexpr: " << _as<std::string>(n.is_constexpr) << "\n";
+ }
+
+ auto start(alternative_node const&, int indent) -> void
+ {
+ o << pre(indent) << "alternative\n";
+ }
+
+ auto start(jump_statement_node const&, int indent) -> void
+ {
+ o << pre(indent) << "jump\n";
+ }
+
+ auto start(using_statement_node const& n, int indent) -> void
+ {
+ o << pre(indent) << "using" << (n.for_namespace? " namespace" : "") << "\n";
+ }
+
+ auto start(inspect_expression_node const& n, int indent) -> void
+ {
+ o << pre(indent) << "inspect-expression\n";
+ o << pre(indent+1) << "is_constexpr: " << _as<std::string>(n.is_constexpr) << "\n";
+ }
+
+ auto start(return_statement_node const&, int indent) -> void
+ {
+ o << pre(indent) << "return-statement\n";
+ }
+
+ auto start(iteration_statement_node const& n, int indent) -> void
+ {
+ o << pre(indent) << "iteration-statement\n";
+ assert(n.identifier);
+ o << pre(indent+1) << "identifier: " << std::string_view(*n.identifier) << "\n";
+ }
+
+ auto start(contract_node const& n, int indent) -> void
+ {
+ o << pre(indent) << "contract\n";
+ assert(n.kind);
+ o << pre(indent+1) << "kind: " << std::string_view(*n.kind) << "\n";
+ if (!n.captures.members.empty()) {
+ o << pre(indent+1) << "captures: " << n.captures.members.size() << "\n";
+ }
+ }
+
+ auto start(type_node const&, int indent) -> void
+ {
+ o << pre(indent) << "user-defined type\n";
+ }
+
+ auto start(namespace_node const&, int indent) -> void
+ {
+ o << pre(indent) << "namespace\n";
+ }
+
+ auto start(function_type_node const& n, int indent) -> void
+ {
+ o << pre(indent) << "function\n";
+ o << pre(indent+1) << "throws: " << _as<std::string>(n.throws) << "\n";
+ if (n.returns.index() == function_type_node::id) {
+ auto& r = std::get<function_type_node::id>(n.returns);
+ if (r.pass != passing_style::invalid) {
+ o << pre(indent+1) << "returns by: " << to_string_view(r.pass) << "\n";
+ }
+ }
+ }
+
+ auto start(function_returns_tag const&, int indent) -> void
+ {
+ o << pre(indent) << "function returns\n";
+ }
+
+ auto start(template_args_tag const&, int indent) -> void
+ {
+ o << pre(indent) << "template arguments\n";
+ }
+
+ auto start(declaration_identifier_tag const&, int indent) -> void
+ {
+ o << pre(indent) << "declaration identifier\n";
+ }
+
+ auto start(next_expression_tag const&, int indent) -> void
+ {
+ o << pre(indent) << "next expression\n";
+ }
+
+ auto start(alias_node const& n, int indent) -> void
+ {
+ o << pre(indent) << "alias\n";
+ switch (n.initializer.index()) {
+ break;case alias_node::a_type:
+ o << pre(indent+1) << "type\n";
+ break;case alias_node::a_namespace:
+ o << pre(indent+1) << "namespace\n";
+ break;case alias_node::an_object:
+ o << pre(indent+1) << "object\n";
+ break;default:
+ o << pre(indent+1) << "ICE - invalid variant state\n";
+ }
+ }
+
+ auto start(declaration_node const& n, int indent) -> void
+ {
+ o << pre(indent) << "declaration [" << &n << "]\n";
+ o << pre(indent+1) << "parent: [" << n.parent_declaration << "]\n";
+ o << pre(indent+1) << "is_variadic: [" << std::boolalpha << n.is_variadic << "]\n";
+ o << pre(indent+1) << "is_constexpr: " << _as<std::string>(n.is_constexpr) << "\n";
+ switch (n.type.index()) {
+ break;case declaration_node::a_function:
+ o << pre(indent+1) << "function\n";
+ break;case declaration_node::an_object:
+ o << pre(indent+1) << "object\n";
+ break;case declaration_node::a_type:
+ o << pre(indent+1) << "type\n";
+ break;case declaration_node::a_namespace:
+ o << pre(indent+1) << "namespace\n";
+ break;case declaration_node::an_alias:
+ o << pre(indent+1) << "alias\n";
+ break;default:
+ o << pre(indent+1) << "ICE - invalid variant state\n";
+ }
+ if (!n.is_default_access()) {
+ o << pre(indent+1) << "access: " << to_string(n.access) << "\n";
+ }
+ if (!n.captures.members.empty()) {
+ o << pre(indent+1) << "captures: " << n.captures.members.size() << "\n";
+ }
+ }
+
+ auto start(parameter_declaration_node const& n, int indent) -> void
+ {
+ o << pre(indent) << "parameter-declaration\n";
+
+ o << pre(indent+1);
+ switch (n.pass) {
+ break;case passing_style::in : o << "in";
+ break;case passing_style::copy : o << "copy";
+ break;case passing_style::inout : o << "inout";
+ break;case passing_style::out : o << "out";
+ break;case passing_style::move : o << "move";
+ break;case passing_style::forward: o << "forward";
+ break;default: ;
+ }
+
+ o << pre(indent+1);
+ switch (n.mod) {
+ break;case parameter_declaration_node::modifier::implicit : o << "implicit";
+ break;case parameter_declaration_node::modifier::virtual_ : o << "virtual";
+ break;case parameter_declaration_node::modifier::override_ : o << "override";
+ break;case parameter_declaration_node::modifier::final_ : o << "final";
+ break;default: ;
+ }
+ o << "\n";
+
+ assert( n.declaration );
+ }
+
+ auto start(parameter_declaration_list_node const&, int indent) -> void
+ {
+ o << pre(indent) << "parameter-declaration-list\n";
+ }
+
+ auto start(translation_unit_node const&, int indent) -> void
+ {
+ o << pre(indent) << "translation-unit\n";
+ }
+
+ auto start(auto const&, int indent) -> void
+ {
+ o << pre(indent) << "UNRECOGNIZED -- FIXME\n";
+ }
+
+ auto end(auto const&, int) -> void
+ {
+ // Ignore other node types
+ }
+};
+
+
+auto parser::debug_print(std::ostream& o)
+
+ -> void
+{
+ o << "\n\n--- Parse tree\n";
+
+ auto tree_printer = parse_tree_printer{o};
+ visit( tree_printer );
+
+ o << "\n\n--- Function body extents\n";
+
+ for (auto const& f : function_body_extents) {
+ o << " " << f.first << "-" << f.last << "\n";
+ }
+}
+
+
+}
+
+#endif