blob: be8ed5609137e06b26ca0b60a27b1a063295b907 [file] [log] [blame]
// Copyright (C) 2020-2023 Free Software Foundation, Inc.
// This file is part of GCC.
// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#ifndef RUST_AST_MACRO_H
#define RUST_AST_MACRO_H
#include "rust-system.h"
#include "rust-ast.h"
#include "rust-ast-fragment.h"
#include "rust-location.h"
#include "rust-item.h"
#include "rust-make-unique.h"
namespace Rust {
namespace AST {
class MacroFragSpec
{
public:
enum Kind
{
BLOCK,
EXPR,
IDENT,
ITEM,
LIFETIME,
LITERAL,
META,
PAT,
PATH,
STMT,
TT,
TY,
VIS,
INVALID // not really a specifier, but used to mark invalid one passed in
};
MacroFragSpec (Kind kind) : kind (kind) {}
static MacroFragSpec get_frag_spec_from_str (const std::string &str)
{
if (str == "block")
return MacroFragSpec (BLOCK);
else if (str == "expr")
return MacroFragSpec (EXPR);
else if (str == "ident")
return MacroFragSpec (IDENT);
else if (str == "item")
return MacroFragSpec (ITEM);
else if (str == "lifetime")
return MacroFragSpec (LIFETIME);
else if (str == "literal")
return MacroFragSpec (LITERAL);
else if (str == "meta")
return MacroFragSpec (META);
else if (str == "pat" || str == "pat_param")
return MacroFragSpec (PAT);
else if (str == "path")
return MacroFragSpec (PATH);
else if (str == "stmt")
return MacroFragSpec (STMT);
else if (str == "tt")
return MacroFragSpec (TT);
else if (str == "ty")
return MacroFragSpec (TY);
else if (str == "vis")
return MacroFragSpec (VIS);
else
{
// error_at("invalid string '%s' used as fragment specifier",
// str->c_str()));
return MacroFragSpec (INVALID);
}
}
Kind get_kind () const { return kind; }
bool is_error () const { return kind == Kind::INVALID; }
// Converts a frag spec enum item to a string form.
std::string as_string () const
{
switch (kind)
{
case BLOCK:
return "block";
case EXPR:
return "expr";
case IDENT:
return "ident";
case ITEM:
return "item";
case LIFETIME:
return "lifetime";
case LITERAL:
return "literal";
case META:
return "meta";
case PAT:
return "pat";
case PATH:
return "path";
case STMT:
return "stmt";
case TT:
return "tt";
case TY:
return "ty";
case VIS:
return "vis";
case INVALID:
return "INVALID_FRAG_SPEC";
default:
return "ERROR_MARK_STRING - unknown frag spec";
}
}
bool has_follow_set_restrictions () const
{
switch (kind)
{
case EXPR:
case STMT:
case PAT:
case PATH:
case TY:
case VIS:
return true;
default:
return false;
}
}
bool has_follow_set_fragment_restrictions () const
{
switch (kind)
{
case PAT:
case TY:
case VIS:
return true;
default:
return false;
}
}
private:
Kind kind;
};
// A macro match that has an identifier and fragment spec
class MacroMatchFragment : public MacroMatch
{
Identifier ident;
MacroFragSpec frag_spec;
Location locus;
public:
MacroMatchFragment (Identifier ident, MacroFragSpec frag_spec, Location locus)
: ident (std::move (ident)), frag_spec (frag_spec), locus (locus)
{}
// Returns whether macro match fragment is in an error state.
bool is_error () const
{
return frag_spec.get_kind () == MacroFragSpec::INVALID;
}
// Creates an error state macro match fragment.
static MacroMatchFragment create_error (Location locus)
{
return MacroMatchFragment (std::string (""),
MacroFragSpec (MacroFragSpec::Kind::INVALID),
locus);
}
std::string as_string () const override;
Location get_match_locus () const override { return locus; };
void accept_vis (ASTVisitor &vis) override;
MacroMatchType get_macro_match_type () const override
{
return MacroMatchType::Fragment;
}
Identifier get_ident () const { return ident; }
const MacroFragSpec &get_frag_spec () const { return frag_spec; }
protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
MacroMatchFragment *clone_macro_match_impl () const override
{
return new MacroMatchFragment (*this);
}
};
// A repetition macro match
class MacroMatchRepetition : public MacroMatch
{
public:
enum MacroRepOp
{
NONE,
ANY,
ONE_OR_MORE,
ZERO_OR_ONE,
};
private:
std::vector<std::unique_ptr<MacroMatch>> matches;
MacroRepOp op;
// bool has_sep;
typedef Token MacroRepSep;
// any token except delimiters and repetition operators
std::unique_ptr<MacroRepSep> sep;
Location locus;
public:
// Returns whether macro match repetition has separator token.
bool has_sep () const { return sep != nullptr; }
MacroMatchRepetition (std::vector<std::unique_ptr<MacroMatch>> matches,
MacroRepOp op, std::unique_ptr<MacroRepSep> sep,
Location locus)
: matches (std::move (matches)), op (op), sep (std::move (sep)),
locus (locus)
{}
// Copy constructor with clone
MacroMatchRepetition (MacroMatchRepetition const &other)
: op (other.op), locus (other.locus)
{
// guard to protect from null pointer dereference
if (other.sep != nullptr)
sep = other.sep->clone_token ();
matches.reserve (other.matches.size ());
for (const auto &e : other.matches)
matches.push_back (e->clone_macro_match ());
}
// Overloaded assignment operator to clone
MacroMatchRepetition &operator= (MacroMatchRepetition const &other)
{
op = other.op;
locus = other.locus;
// guard to protect from null pointer dereference
if (other.sep != nullptr)
sep = other.sep->clone_token ();
else
sep = nullptr;
matches.reserve (other.matches.size ());
for (const auto &e : other.matches)
matches.push_back (e->clone_macro_match ());
return *this;
}
// move constructors
MacroMatchRepetition (MacroMatchRepetition &&other) = default;
MacroMatchRepetition &operator= (MacroMatchRepetition &&other) = default;
std::string as_string () const override;
Location get_match_locus () const override { return locus; };
void accept_vis (ASTVisitor &vis) override;
MacroMatchType get_macro_match_type () const override
{
return MacroMatchType::Repetition;
}
MacroRepOp get_op () const { return op; }
const std::unique_ptr<MacroRepSep> &get_sep () const { return sep; }
std::vector<std::unique_ptr<MacroMatch>> &get_matches () { return matches; }
const std::vector<std::unique_ptr<MacroMatch>> &get_matches () const
{
return matches;
}
protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
MacroMatchRepetition *clone_macro_match_impl () const override
{
return new MacroMatchRepetition (*this);
}
};
// can't inline due to polymorphism
class MacroMatcher : public MacroMatch
{
DelimType delim_type;
std::vector<std::unique_ptr<MacroMatch>> matches;
Location locus;
// TODO: think of way to mark invalid that doesn't take up more space
bool is_invalid;
public:
MacroMatcher (DelimType delim_type,
std::vector<std::unique_ptr<MacroMatch>> matches,
Location locus)
: delim_type (delim_type), matches (std::move (matches)), locus (locus),
is_invalid (false)
{}
// copy constructor with vector clone
MacroMatcher (MacroMatcher const &other)
: delim_type (other.delim_type), locus (other.locus)
{
matches.reserve (other.matches.size ());
for (const auto &e : other.matches)
matches.push_back (e->clone_macro_match ());
}
// overloaded assignment operator with vector clone
MacroMatcher &operator= (MacroMatcher const &other)
{
delim_type = other.delim_type;
locus = other.locus;
matches.reserve (other.matches.size ());
for (const auto &e : other.matches)
matches.push_back (e->clone_macro_match ());
return *this;
}
// move constructors
MacroMatcher (MacroMatcher &&other) = default;
MacroMatcher &operator= (MacroMatcher &&other) = default;
// Creates an error state macro matcher.
static MacroMatcher create_error (Location locus)
{
return MacroMatcher (true, locus);
}
// Returns whether MacroMatcher is in an error state.
bool is_error () const { return is_invalid; }
Location get_match_locus () const override { return locus; }
std::string as_string () const override;
void accept_vis (ASTVisitor &vis) override;
MacroMatchType get_macro_match_type () const override
{
return MacroMatchType::Matcher;
}
DelimType get_delim_type () const { return delim_type; }
std::vector<std::unique_ptr<MacroMatch>> &get_matches () { return matches; }
const std::vector<std::unique_ptr<MacroMatch>> &get_matches () const
{
return matches;
}
protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
MacroMatcher *clone_macro_match_impl () const override
{
return new MacroMatcher (*this);
}
// constructor only used to create error matcher
MacroMatcher (bool is_invalid, Location locus)
: delim_type (PARENS), locus (locus), is_invalid (is_invalid)
{}
};
// TODO: inline?
struct MacroTranscriber
{
private:
DelimTokenTree token_tree;
Location locus;
public:
MacroTranscriber (DelimTokenTree token_tree, Location locus)
: token_tree (std::move (token_tree)), locus (locus)
{}
std::string as_string () const { return token_tree.as_string (); }
Location get_locus () const { return locus; }
DelimTokenTree &get_token_tree () { return token_tree; }
const DelimTokenTree &get_token_tree () const { return token_tree; }
};
// A macro rule? Matcher and transcriber pair?
struct MacroRule
{
private:
MacroMatcher matcher;
MacroTranscriber transcriber;
Location locus;
public:
MacroRule (MacroMatcher matcher, MacroTranscriber transcriber, Location locus)
: matcher (std::move (matcher)), transcriber (std::move (transcriber)),
locus (locus)
{}
// Returns whether macro rule is in error state.
bool is_error () const { return matcher.is_error (); }
// Creates an error state macro rule.
static MacroRule create_error (Location locus)
{
return MacroRule (MacroMatcher::create_error (locus),
MacroTranscriber (DelimTokenTree::create_empty (),
Location ()),
locus);
}
Location get_locus () const { return locus; }
std::string as_string () const;
MacroMatcher &get_matcher () { return matcher; }
MacroTranscriber &get_transcriber () { return transcriber; }
};
// A macro rules definition item AST node
class MacroRulesDefinition : public VisItem
{
public:
enum MacroKind
{
// Macro by Example (legacy macro rules)
MBE,
// Declarative macros 2.0
DeclMacro,
};
private:
std::vector<Attribute> outer_attrs;
Identifier rule_name;
// MacroRulesDef rules_def;
// only curly without required semicolon at end
DelimType delim_type;
// MacroRules rules;
std::vector<MacroRule> rules; // inlined form
Location locus;
std::function<Fragment (Location, MacroInvocData &)> associated_transcriber;
// Since we can't compare std::functions, we need to use an extra boolean
bool is_builtin_rule;
MacroKind kind;
/**
* Default function to use as an associated transcriber. This function should
* never be called, hence the gcc_unreachable().
* If this function is used, then the macro is not builtin and the compiler
* should make use of the actual rules. If the macro is builtin, then another
* associated transcriber should be used
*/
static Fragment dummy_builtin (Location, MacroInvocData &)
{
gcc_unreachable ();
return Fragment::create_error ();
}
/* NOTE: in rustc, macro definitions are considered (and parsed as) a type
* of macro, whereas here they are considered part of the language itself.
* I am not aware of the implications of this decision. The rustc spec does
* mention that using the same parser for macro definitions and invocations
* is "extremely self-referential and non-intuitive". */
MacroRulesDefinition (Identifier rule_name, DelimType delim_type,
std::vector<MacroRule> rules,
std::vector<Attribute> outer_attrs, Location locus,
MacroKind kind, Visibility vis)
: VisItem (std::move (vis), outer_attrs),
outer_attrs (std::move (outer_attrs)), rule_name (std::move (rule_name)),
delim_type (delim_type), rules (std::move (rules)), locus (locus),
associated_transcriber (dummy_builtin), is_builtin_rule (false),
kind (kind)
{}
MacroRulesDefinition (
Identifier builtin_name, DelimType delim_type,
std::function<Fragment (Location, MacroInvocData &)> associated_transcriber,
MacroKind kind, Visibility vis)
: VisItem (std::move (vis), std::vector<Attribute> ()),
outer_attrs (std::vector<Attribute> ()), rule_name (builtin_name),
delim_type (delim_type), rules (std::vector<MacroRule> ()),
locus (Location ()), associated_transcriber (associated_transcriber),
is_builtin_rule (true), kind (kind)
{}
public:
std::string as_string () const override;
static std::unique_ptr<MacroRulesDefinition>
mbe (Identifier rule_name, DelimType delim_type, std::vector<MacroRule> rules,
std::vector<Attribute> outer_attrs, Location locus)
{
return Rust::make_unique<MacroRulesDefinition> (
MacroRulesDefinition (rule_name, delim_type, rules, outer_attrs, locus,
AST::MacroRulesDefinition::MacroKind::MBE,
AST::Visibility::create_error ()));
}
static std::unique_ptr<MacroRulesDefinition>
decl_macro (Identifier rule_name, std::vector<MacroRule> rules,
std::vector<Attribute> outer_attrs, Location locus,
Visibility vis)
{
return Rust::make_unique<MacroRulesDefinition> (MacroRulesDefinition (
rule_name, AST::DelimType::CURLY, rules, outer_attrs, locus,
AST::MacroRulesDefinition::MacroKind::DeclMacro, vis));
}
void accept_vis (ASTVisitor &vis) override;
// Invalid if rule name is empty, so base stripping on that.
void mark_for_strip () override { rule_name = ""; }
bool is_marked_for_strip () const override { return rule_name.empty (); }
// TODO: this mutable getter seems really dodgy. Think up better way.
std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
std::vector<MacroRule> &get_macro_rules () { return rules; }
const std::vector<MacroRule> &get_macro_rules () const { return rules; }
Location get_locus () const override final { return locus; }
Identifier get_rule_name () const { return rule_name; }
std::vector<MacroRule> &get_rules () { return rules; }
const std::vector<MacroRule> &get_rules () const { return rules; }
bool is_builtin () const { return is_builtin_rule; }
const std::function<Fragment (Location, MacroInvocData &)> &
get_builtin_transcriber () const
{
rust_assert (is_builtin ());
return associated_transcriber;
}
void set_builtin_transcriber (
std::function<Fragment (Location, MacroInvocData &)> transcriber)
{
associated_transcriber = transcriber;
is_builtin_rule = true;
}
Kind get_ast_kind () const override { return Kind::MACRO_RULES_DEFINITION; }
protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
MacroRulesDefinition *clone_item_impl () const override
{
return new MacroRulesDefinition (*this);
}
};
/**
* All builtin macros possible
*/
enum class BuiltinMacro
{
Assert,
File,
Line,
Column,
IncludeBytes,
IncludeStr,
CompileError,
Concat,
Env,
Cfg,
Include
};
BuiltinMacro
builtin_macro_from_string (const std::string &identifier);
/* AST node of a macro invocation, which is replaced by the macro result at
* compile time. This is technically a sum-type/tagged-union, which represents
* both classic macro invocations and builtin macro invocations. Regular macro
* invocations are expanded lazily, but builtin macro invocations need to be
* expanded eagerly, hence the differentiation.
*/
class MacroInvocation : public TypeNoBounds,
public Pattern,
public Item,
public TraitItem,
public TraitImplItem,
public InherentImplItem,
public ExternalItem,
public ExprWithoutBlock
{
public:
enum class InvocKind
{
Regular,
Builtin,
};
std::string as_string () const override;
/**
* The default constructor you should use. Whenever we parse a macro call, we
* cannot possibly know whether or not this call refers to a builtin macro or
* a regular macro. With name resolution and scopes and nested macro calls,
* this is literally impossible. Hence, always start by creating a `Regular`
* MacroInvocation which will then (maybe!) become a `Builtin` macro
* invocation in the expander.
*/
static std::unique_ptr<MacroInvocation>
Regular (MacroInvocData invoc_data, std::vector<Attribute> outer_attrs,
Location locus, bool is_semi_coloned = false)
{
return std::unique_ptr<MacroInvocation> (
new MacroInvocation (InvocKind::Regular, Optional<BuiltinMacro>::none (),
invoc_data, outer_attrs, locus, is_semi_coloned,
{}));
}
/**
* Create a builtin macro invocation. This can only be done after macro
* name-resolution and within the macro expander, so unless you're modifying
* these visitors, you probably do not want to use this function.
*/
static std::unique_ptr<MacroInvocation> Builtin (
BuiltinMacro kind, MacroInvocData invoc_data,
std::vector<Attribute> outer_attrs, Location locus,
std::vector<std::unique_ptr<MacroInvocation>> &&pending_eager_invocations
= {},
bool is_semi_coloned = false)
{
return std::unique_ptr<MacroInvocation> (
new MacroInvocation (InvocKind::Builtin,
Optional<BuiltinMacro>::some (kind), invoc_data,
outer_attrs, locus, is_semi_coloned,
std::move (pending_eager_invocations)));
}
Location get_locus () const override final { return locus; }
void accept_vis (ASTVisitor &vis) override;
// Invalid if path is empty, so base stripping on that.
void mark_for_strip () override { invoc_data.mark_for_strip (); }
bool is_marked_for_strip () const override
{
return invoc_data.is_marked_for_strip ();
}
const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
void set_outer_attrs (std::vector<Attribute> new_attrs) override
{
outer_attrs = std::move (new_attrs);
}
NodeId get_pattern_node_id () const override final
{
return ExprWithoutBlock::get_node_id ();
}
Kind get_ast_kind () const override { return Kind::MACRO_INVOCATION; }
NodeId get_macro_node_id () const { return node_id; }
MacroInvocData &get_invoc_data () { return invoc_data; }
bool has_semicolon () const { return is_semi_coloned; }
InvocKind get_kind () const { return kind; }
Optional<BuiltinMacro> get_builtin_kind () const { return builtin_kind; }
/**
* Turn the current MacroInvocation into a builtin macro invocation
*/
void map_to_builtin (BuiltinMacro macro)
{
kind = InvocKind::Builtin;
builtin_kind = Optional<BuiltinMacro>::some (macro);
}
/**
* Get the list of pending macro invcations within the builtin macro
* invocation that should get expanded eagerly.
*/
std::vector<std::unique_ptr<MacroInvocation>> &
get_pending_eager_invocations ()
{
rust_assert (kind == InvocKind::Builtin);
return pending_eager_invocs;
}
private:
/* Full constructor */
MacroInvocation (
InvocKind kind, Optional<BuiltinMacro> builtin_kind,
MacroInvocData invoc_data, std::vector<Attribute> outer_attrs,
Location locus, bool is_semi_coloned,
std::vector<std::unique_ptr<MacroInvocation>> &&pending_eager_invocs)
: TraitItem (locus), outer_attrs (std::move (outer_attrs)), locus (locus),
node_id (Analysis::Mappings::get ()->get_next_node_id ()),
invoc_data (std::move (invoc_data)), is_semi_coloned (is_semi_coloned),
kind (kind), builtin_kind (builtin_kind),
pending_eager_invocs (std::move (pending_eager_invocs))
{}
MacroInvocation (const MacroInvocation &other)
: TraitItem (other.locus), outer_attrs (other.outer_attrs),
locus (other.locus), node_id (other.node_id),
invoc_data (other.invoc_data), is_semi_coloned (other.is_semi_coloned),
kind (other.kind), builtin_kind (other.builtin_kind)
{
if (other.kind == InvocKind::Builtin)
for (auto &pending : other.pending_eager_invocs)
pending_eager_invocs.emplace_back (
pending->clone_macro_invocation_impl ());
}
std::vector<Attribute> outer_attrs;
Location locus;
NodeId node_id;
/* The data given to the macro invocation */
MacroInvocData invoc_data;
/* Important for when we actually expand the macro */
bool is_semi_coloned;
/* Is this a builtin macro or a regular macro */
InvocKind kind;
/* If it is a builtin macro, which one */
Optional<BuiltinMacro> builtin_kind = Optional<BuiltinMacro>::none ();
/**
* Pending invocations within a builtin macro invocation. This vector is empty
* and should not be accessed for a regular macro invocation. The macro
* invocations within should be name resolved and expanded before the builtin
* macro invocation get expanded again. It is then the role of the expander to
* insert these new tokens properly in the delimited token tree and try the
* builtin transcriber once again.
*/
std::vector<std::unique_ptr<MacroInvocation>> pending_eager_invocs;
protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
MacroInvocation *clone_pattern_impl () const final override
{
return clone_macro_invocation_impl ();
}
/* Use covariance to implement clone function as returning this object rather
* than base */
MacroInvocation *clone_expr_without_block_impl () const final override
{
return clone_macro_invocation_impl ();
}
/* Use covariance to implement clone function as returning this object rather
* than base */
MacroInvocation *clone_type_no_bounds_impl () const final override
{
return clone_macro_invocation_impl ();
}
MacroInvocation *clone_external_item_impl () const final override
{
return clone_macro_invocation_impl ();
}
/*virtual*/ MacroInvocation *clone_macro_invocation_impl () const
{
return new MacroInvocation (*this);
}
Item *clone_item_impl () const override
{
return clone_macro_invocation_impl ();
}
bool is_item () const override { return !has_semicolon (); }
TraitItem *clone_trait_item_impl () const override
{
return clone_macro_invocation_impl ();
};
TraitImplItem *clone_trait_impl_item_impl () const override
{
return clone_macro_invocation_impl ();
};
InherentImplItem *clone_inherent_impl_item_impl () const override
{
return clone_macro_invocation_impl ();
}
ExprWithoutBlock *to_stmt () const override
{
auto new_impl = clone_macro_invocation_impl ();
new_impl->is_semi_coloned = true;
return new_impl;
}
};
// more generic meta item path-only form
class MetaItemPath : public MetaItem
{
SimplePath path;
public:
MetaItemPath (SimplePath path) : path (std::move (path)) {}
std::string as_string () const override { return path.as_string (); }
void accept_vis (ASTVisitor &vis) override;
// HACK: used to simplify parsing - returns non-empty only in this case
SimplePath to_path_item () const override
{
// this should copy construct - TODO ensure it does
return path;
}
Location get_locus () const override { return path.get_locus (); }
bool check_cfg_predicate (const Session &session) const override;
Attribute to_attribute () const override;
protected:
// Use covariance to implement clone function as returning this type
MetaItemPath *clone_meta_item_inner_impl () const override
{
return new MetaItemPath (*this);
}
};
// more generic meta item sequence form
class MetaItemSeq : public MetaItem
{
SimplePath path;
std::vector<std::unique_ptr<MetaItemInner>> seq;
public:
MetaItemSeq (SimplePath path, std::vector<std::unique_ptr<MetaItemInner>> seq)
: path (std::move (path)), seq (std::move (seq))
{}
// copy constructor with vector clone
MetaItemSeq (const MetaItemSeq &other) : path (other.path)
{
seq.reserve (other.seq.size ());
for (const auto &e : other.seq)
seq.push_back (e->clone_meta_item_inner ());
}
// overloaded assignment operator with vector clone
MetaItemSeq &operator= (const MetaItemSeq &other)
{
MetaItem::operator= (other);
path = other.path;
seq.reserve (other.seq.size ());
for (const auto &e : other.seq)
seq.push_back (e->clone_meta_item_inner ());
return *this;
}
// default move constructors
MetaItemSeq (MetaItemSeq &&other) = default;
MetaItemSeq &operator= (MetaItemSeq &&other) = default;
std::string as_string () const override;
void accept_vis (ASTVisitor &vis) override;
Location get_locus () const override { return path.get_locus (); }
bool check_cfg_predicate (const Session &session) const override;
Attribute to_attribute () const override;
protected:
// Use covariance to implement clone function as returning this type
MetaItemSeq *clone_meta_item_inner_impl () const override
{
return new MetaItemSeq (*this);
}
};
// Preferred specialisation for single-identifier meta items.
class MetaWord : public MetaItem
{
Identifier ident;
Location ident_locus;
public:
MetaWord (Identifier ident, Location ident_locus)
: ident (std::move (ident)), ident_locus (ident_locus)
{}
std::string as_string () const override { return ident; }
void accept_vis (ASTVisitor &vis) override;
Location get_locus () const override { return ident_locus; }
bool check_cfg_predicate (const Session &session) const override;
Attribute to_attribute () const override;
protected:
// Use covariance to implement clone function as returning this type
MetaWord *clone_meta_item_inner_impl () const override
{
return new MetaWord (*this);
}
};
// Preferred specialisation for "identifier '=' string literal" meta items.
class MetaNameValueStr : public MetaItem
{
Identifier ident;
Location ident_locus;
// NOTE: str stored without quotes
std::string str;
Location str_locus;
public:
MetaNameValueStr (Identifier ident, Location ident_locus, std::string str,
Location str_locus)
: ident (std::move (ident)), ident_locus (ident_locus),
str (std::move (str)), str_locus (str_locus)
{}
std::string as_string () const override
{
return ident + " = \"" + str + "\"";
}
void accept_vis (ASTVisitor &vis) override;
// HACK: used to simplify parsing - creates a copy of this
std::unique_ptr<MetaNameValueStr> to_meta_name_value_str () const override
{
return std::unique_ptr<MetaNameValueStr> (clone_meta_item_inner_impl ());
}
Location get_locus () const override { return ident_locus; }
bool check_cfg_predicate (const Session &session) const override;
Attribute to_attribute () const override;
inline std::pair<Identifier, std::string> get_name_value_pair () const
{
return std::pair<Identifier, std::string> (ident, str);
}
bool is_key_value_pair () const override { return true; }
protected:
// Use covariance to implement clone function as returning this type
MetaNameValueStr *clone_meta_item_inner_impl () const override
{
return new MetaNameValueStr (*this);
}
};
// doubles up as MetaListIdents - determine via iterating through each path?
// Preferred specialisation for "identifier '(' SimplePath, SimplePath, ... ')'"
class MetaListPaths : public MetaItem
{
Identifier ident;
Location ident_locus;
std::vector<SimplePath> paths;
public:
MetaListPaths (Identifier ident, Location ident_locus,
std::vector<SimplePath> paths)
: ident (std::move (ident)), ident_locus (ident_locus),
paths (std::move (paths))
{}
std::string as_string () const override;
void accept_vis (ASTVisitor &vis) override;
Location get_locus () const override { return ident_locus; }
bool check_cfg_predicate (const Session &session) const override;
Attribute to_attribute () const override;
private:
bool check_path_exists_in_cfg (const Session &session,
const SimplePath &path) const;
protected:
// Use covariance to implement clone function as returning this type
MetaListPaths *clone_meta_item_inner_impl () const override
{
return new MetaListPaths (*this);
}
};
// Preferred specialisation for "identifier '(' MetaNameValueStr, ... ')'"
class MetaListNameValueStr : public MetaItem
{
Identifier ident;
Location ident_locus;
std::vector<MetaNameValueStr> strs;
public:
MetaListNameValueStr (Identifier ident, Location ident_locus,
std::vector<MetaNameValueStr> strs)
: ident (std::move (ident)), ident_locus (ident_locus),
strs (std::move (strs))
{}
std::string as_string () const override;
void accept_vis (ASTVisitor &vis) override;
Location get_locus () const override { return ident_locus; }
bool check_cfg_predicate (const Session &session) const override;
Attribute to_attribute () const override;
protected:
// Use covariance to implement clone function as returning this type
MetaListNameValueStr *clone_meta_item_inner_impl () const override
{
return new MetaListNameValueStr (*this);
}
};
// Object that parses macros from a token stream.
/* TODO: would "AttributeParser" be a better name? MetaItems are only for
* attributes, I believe */
struct AttributeParser
{
private:
// TODO: might as well rewrite to use lexer tokens
std::vector<std::unique_ptr<Token>> token_stream;
int stream_pos;
public:
AttributeParser (std::vector<std::unique_ptr<Token>> token_stream,
int stream_start_pos = 0)
: token_stream (std::move (token_stream)), stream_pos (stream_start_pos)
{}
~AttributeParser () = default;
std::vector<std::unique_ptr<MetaItemInner>> parse_meta_item_seq ();
private:
// Parses a MetaItemInner.
std::unique_ptr<MetaItemInner> parse_meta_item_inner ();
// Returns whether token can end a meta item.
bool is_end_meta_item_tok (TokenId id) const;
// Parses a simple path.
SimplePath parse_simple_path ();
// Parses a segment of a simple path (but not scope resolution operator).
SimplePathSegment parse_simple_path_segment ();
// Parses a MetaItemLitExpr.
std::unique_ptr<MetaItemLitExpr> parse_meta_item_lit ();
// Parses a literal.
Literal parse_literal ();
// Parses a meta item that begins with a simple path.
std::unique_ptr<MetaItem> parse_path_meta_item ();
// TODO: should this be const?
std::unique_ptr<Token> &peek_token (int i = 0)
{
return token_stream[stream_pos + i];
}
void skip_token (int i = 0) { stream_pos += 1 + i; }
};
} // namespace AST
} // namespace Rust
/* <https://stackoverflow.com/a/35304501> */
namespace std {
template <> struct hash<Rust::AST::MacroFragSpec::Kind>
{
size_t operator() (const Rust::AST::MacroFragSpec::Kind &t) const noexcept
{
return size_t (t);
}
};
} // namespace std
#endif