// Copyright (C) 2020-2022 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
// .
#ifndef RUST_MACRO_EXPAND_H
#define RUST_MACRO_EXPAND_H
#include "rust-buffered-queue.h"
#include "rust-parse.h"
#include "rust-token.h"
#include "rust-ast.h"
#include "rust-macro.h"
#include "rust-hir-map.h"
#include "rust-name-resolver.h"
#include "rust-macro-invoc-lexer.h"
// Provides objects and method prototypes for macro expansion
namespace Rust {
// forward decls for AST
namespace AST {
class MacroInvocation;
}
// Object used to store configuration data for macro expansion.
// NOTE: Keep all these items complying with the latest rustc.
struct ExpansionCfg
{
// features?
// TODO: Add `features' when we have it.
unsigned int recursion_limit = 1024;
bool trace_mac = false; // trace macro
bool should_test = false; // strip #[test] nodes if false
bool keep_macs = false; // keep macro definitions
std::string crate_name = "";
};
struct MatchedFragment
{
std::string fragment_ident;
size_t token_offset_begin;
size_t token_offset_end;
size_t match_amount;
MatchedFragment (std::string identifier, size_t token_offset_begin,
size_t token_offset_end, size_t match_amount = 1)
: fragment_ident (identifier), token_offset_begin (token_offset_begin),
token_offset_end (token_offset_end), match_amount (match_amount)
{}
/**
* Create a valid fragment matched zero times. This is useful for repetitions
* which allow the absence of a fragment, such as * and ?
*/
static MatchedFragment zero (std::string identifier)
{
// We don't need offsets since there is "no match"
return MatchedFragment (identifier, 0, 0, 0);
}
std::string as_string () const
{
return fragment_ident + "=" + std::to_string (token_offset_begin) + ":"
+ std::to_string (token_offset_end) + " (matched "
+ std::to_string (match_amount) + " times)";
}
void set_match_amount (size_t new_amount) { match_amount = new_amount; }
};
class SubstitutionScope
{
public:
SubstitutionScope () : stack () {}
void push () { stack.push_back ({}); }
std::map pop ()
{
auto top = stack.back ();
stack.pop_back ();
return top;
}
std::map &peek () { return stack.back (); }
private:
std::vector> stack;
};
// Object used to store shared data (between functions) for macro expansion.
struct MacroExpander
{
enum ContextType
{
ITEM,
BLOCK,
};
ExpansionCfg cfg;
unsigned int expansion_depth = 0;
MacroExpander (AST::Crate &crate, ExpansionCfg cfg, Session &session)
: cfg (cfg), crate (crate), session (session),
sub_stack (SubstitutionScope ()), resolver (Resolver::Resolver::get ()),
mappings (Analysis::Mappings::get ())
{}
~MacroExpander () = default;
// Expands all macros in the crate passed in.
void expand_crate ();
/* Expands a macro invocation (not macro invocation semi) - possibly make both
* have similar duck-typed interface and use templates?*/
// should this be public or private?
void expand_invoc (AST::MacroInvocation &invoc);
void expand_invoc_semi (AST::MacroInvocationSemi &invoc);
// Expands a single declarative macro.
AST::ASTFragment expand_decl_macro (Location locus,
AST::MacroInvocData &invoc,
AST::MacroRulesDefinition &rules_def,
bool semicolon);
void expand_cfg_attrs (AST::AttrVec &attrs);
bool fails_cfg (const AST::AttrVec &attr) const;
bool fails_cfg_with_expand (AST::AttrVec &attrs) const;
// Expand the data of a cfg! macro.
void parse_macro_to_meta_item (AST::MacroInvocData &invoc);
// Get the literal representation of a cfg! macro.
AST::Literal expand_cfg_macro (AST::MacroInvocData &invoc);
bool depth_exceeds_recursion_limit () const;
bool try_match_rule (AST::MacroRule &match_rule,
AST::DelimTokenTree &invoc_token_tree);
AST::ASTFragment
transcribe_rule (AST::MacroRule &match_rule,
AST::DelimTokenTree &invoc_token_tree,
std::map &matched_fragments,
bool semicolon, ContextType ctx);
bool match_fragment (Parser &parser,
AST::MacroMatchFragment &fragment);
bool match_token (Parser &parser, AST::Token &token);
bool match_repetition (Parser &parser,
AST::MacroMatchRepetition &rep);
bool match_matcher (Parser &parser,
AST::MacroMatcher &matcher);
/**
* Match any amount of matches
*
* @param parser Parser to use for matching
* @param matches All consecutive matches to identify
* @param match_amount Reference in which to store the ammount of succesful
* and valid matches
*
* @param lo_bound Lower bound of the matcher. When specified, the matcher
* will only succeed if it parses at *least* `lo_bound` fragments. If
* unspecified, the matcher could succeed when parsing 0 fragments.
*
* @param hi_bound Higher bound of the matcher. When specified, the matcher
* will only succeed if it parses *less than* `hi_bound` fragments. If
* unspecified, the matcher could succeed when parsing an infinity of
* fragments.
*
* @return true if matching was successful and within the given limits, false
* otherwise
*/
bool match_n_matches (Parser &parser,
std::vector> &matches,
size_t &match_amount, size_t lo_bound = 0,
size_t hi_bound = 0);
/**
* Substitute a metavariable by its given fragment in a transcribing context,
* i.e. replacing $var with the associated fragment.
*
* @param input Tokens given to the transcribing context
* @param fragments Fragments given to the macro substitution
* @param metavar Metavariable to try and replace
*
* @return A token containing the associated fragment expanded into tokens if
* any, or the cloned token if no fragment was associated
*/
static std::vector>
substitute_metavar (std::vector> &input,
std::map &fragments,
std::unique_ptr &metavar);
/**
* Substitute a macro repetition by its given fragments
*
* @param input Tokens given to the transcribing context
* @param fragments Fragments given to the macro substitution
* @param pattern_start Start index of the pattern tokens
* @param pattern_end Index Amount of tokens in the pattern
*
* @return A vector containing the repeated pattern
*/
static std::vector>
substitute_repetition (std::vector> &input,
std::vector> ¯o,
std::map &fragments,
size_t pattern_start, size_t pattern_end);
/**
* Substitute a given token by its appropriate representation
*
* @param macro Tokens used in the macro declaration
* @param input Tokens given to the transcribing context
* @param fragments Fragments given to the macro substitution
* @param token Current token to try and substitute
*
* @return A token containing the associated fragment expanded into tokens if
* any, or the cloned token if no fragment was associated, as well as the
* amount of tokens that should be skipped before the next invocation. Since
* this function may consume more than just one token, it is important to skip
* ahead of the input to avoid mis-substitutions
*/
static std::pair>, size_t>
substitute_token (std::vector> &input,
std::vector> ¯o,
std::map &fragments,
size_t token_idx);
static std::vector>
substitute_tokens (std::vector> &input,
std::vector> ¯o,
std::map &fragments);
void push_context (ContextType t) { context.push_back (t); }
ContextType pop_context ()
{
ContextType t = context.back ();
context.pop_back ();
return t;
}
ContextType peek_context () { return context.back (); }
private:
AST::Crate &crate;
Session &session;
SubstitutionScope sub_stack;
std::vector context;
public:
Resolver::Resolver *resolver;
Analysis::Mappings *mappings;
};
} // namespace Rust
#endif