#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 {
-
-// Decls as definitions moved to rust-ast.h
-class MacroItem;
-
class MacroFragSpec
{
public:
};
// A macro rules definition item AST node
-class MacroRulesDefinition : public MacroItem
+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;
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
* 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". */
-
-public:
- std::string as_string () const override;
-
MacroRulesDefinition (Identifier rule_name, DelimType delim_type,
std::vector<MacroRule> rules,
- std::vector<Attribute> outer_attrs, Location locus)
- : outer_attrs (std::move (outer_attrs)), rule_name (std::move (rule_name)),
+ 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)
+ associated_transcriber (dummy_builtin), is_builtin_rule (false),
+ kind (kind)
{}
MacroRulesDefinition (
Identifier builtin_name, DelimType delim_type,
- std::function<Fragment (Location, MacroInvocData &)> associated_transcriber)
- : outer_attrs (std::vector<Attribute> ()), rule_name (builtin_name),
+ 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)
+ 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.
* compile time */
class MacroInvocation : public TypeNoBounds,
public Pattern,
- public MacroItem,
+ public Item,
public TraitItem,
public TraitImplItem,
public InherentImplItem,
// parse outer attributes for item
AST::AttrVec outer_attrs = parse_outer_attributes ();
-
- // TODO: decide how to deal with VisItem vs MacroItem dichotomy
- /* best current solution: catch all keywords that would imply a VisItem in a
- * switch and have MacroItem as a last resort */
-
const_TokenPtr t = lexer.peek_token ();
switch (t->get_id ())
case STATIC_TOK:
case TRAIT:
case IMPL:
+ case MACRO:
/* TODO: implement union keyword but not really because of
* context-dependence crappy hack way to parse a union written below to
* separate it from the good code. */
case CRATE:
case DOLLAR_SIGN:
// almost certainly macro invocation semi
- return parse_macro_item (std::move (outer_attrs));
+ return parse_macro_invocation_semi (std::move (outer_attrs));
break;
// crappy hack to do union "keyword"
case IDENTIFIER:
else if (t->get_str () == "macro_rules")
{
// macro_rules! macro item
- return parse_macro_item (std::move (outer_attrs));
+ return parse_macro_rules_def (std::move (outer_attrs));
}
else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION
|| lexer.peek_token (1)->get_id () == EXCLAM)
{
/* path (probably) or macro invocation, so probably a macro invocation
* semi */
- return parse_macro_item (std::move (outer_attrs));
+ return parse_macro_invocation_semi (std::move (outer_attrs));
}
gcc_fallthrough ();
default:
// otherwise unrecognised
- // return parse_macro_item(std::move(outer_attrs));
add_error (Error (t->get_locus (),
"unrecognised token %qs for start of %s",
t->get_token_description (),
lexer.skip_token (1); // TODO: is this right thing to do?
return nullptr;
}
+ case MACRO:
+ return parse_decl_macro_def (std::move (vis), std::move (outer_attrs));
default:
// otherwise vis item clearly doesn't exist, which is not an error
// has a catch-all post-switch return to allow other breaks to occur
return nullptr;
}
-// Parses a MacroItem (either a MacroInvocationSemi or MacroRulesDefinition).
-template <typename ManagedTokenSource>
-std::unique_ptr<AST::MacroItem>
-Parser<ManagedTokenSource>::parse_macro_item (AST::AttrVec outer_attrs)
-{
- const_TokenPtr t = lexer.peek_token ();
-
- /* dodgy way of detecting macro due to weird context-dependence thing.
- * probably can be improved */
- // TODO: ensure that string compare works properly
- if (t->get_id () == IDENTIFIER && t->get_str () == "macro_rules")
- {
- return parse_macro_rules_def (std::move (outer_attrs));
- }
- else
- {
- // DEBUG: TODO: remove
- rust_debug (
- "DEBUG - parse_macro_item called and token is not macro_rules");
- if (t->get_id () == IDENTIFIER)
- {
- rust_debug ("just add to last error: token is not macro_rules and is "
- "instead '%s'",
- t->get_str ().c_str ());
- }
- else
- {
- rust_debug ("just add to last error: token is not macro_rules and is "
- "not an identifier either - it is '%s'",
- t->get_token_description ());
- }
-
- return parse_macro_invocation_semi (std::move (outer_attrs));
- }
-}
-
// Parses a macro rules definition syntax extension whatever thing.
template <typename ManagedTokenSource>
std::unique_ptr<AST::MacroRulesDefinition>
{
// as this is the end, allow recovery (probably) - may change
return std::unique_ptr<AST::MacroRulesDefinition> (
- new AST::MacroRulesDefinition (
+ AST::MacroRulesDefinition::mbe (
std::move (rule_name), delim_type, std::move (macro_rules),
std::move (outer_attrs), macro_locus));
}
}
return std::unique_ptr<AST::MacroRulesDefinition> (
- new AST::MacroRulesDefinition (std::move (rule_name), delim_type,
- std::move (macro_rules),
- std::move (outer_attrs), macro_locus));
+ AST::MacroRulesDefinition::mbe (std::move (rule_name), delim_type,
+ std::move (macro_rules),
+ std::move (outer_attrs), macro_locus));
}
else
{
}
}
+// Parses a declarative macro 2.0 definition.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MacroRulesDefinition>
+Parser<ManagedTokenSource>::parse_decl_macro_def (AST::Visibility vis,
+ AST::AttrVec outer_attrs)
+{
+ // ensure that first token is identifier saying "macro"
+ const_TokenPtr t = lexer.peek_token ();
+ if (t->get_id () != MACRO)
+ {
+ Error error (
+ t->get_locus (),
+ "declarative macro definition does not start with %<macro%>");
+ add_error (std::move (error));
+
+ // skip after somewhere?
+ return nullptr;
+ }
+ lexer.skip_token ();
+ Location macro_locus = t->get_locus ();
+
+ // parse macro name
+ const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+ if (ident_tok == nullptr)
+ {
+ return nullptr;
+ }
+ Identifier rule_name = ident_tok->get_str ();
+
+ t = lexer.peek_token ();
+ if (t->get_id () == LEFT_PAREN)
+ {
+ // single definiton of macro rule
+ // e.g. `macro foo($e:expr) {}`
+
+ // parse macro matcher
+ Location locus = lexer.peek_token ()->get_locus ();
+ AST::MacroMatcher matcher = parse_macro_matcher ();
+ if (matcher.is_error ())
+ return nullptr;
+
+ // check delimiter of macro matcher
+ if (matcher.get_delim_type () != AST::DelimType::PARENS)
+ {
+ Error error (locus, "only parenthesis can be used for a macro "
+ "matcher in declarative macro definition");
+ add_error (std::move (error));
+ return nullptr;
+ }
+
+ Location transcriber_loc = lexer.peek_token ()->get_locus ();
+ AST::DelimTokenTree delim_tok_tree = parse_delim_token_tree ();
+ AST::MacroTranscriber transcriber (delim_tok_tree, transcriber_loc);
+
+ if (transcriber.get_token_tree ().get_delim_type ()
+ != AST::DelimType::CURLY)
+ {
+ Error error (transcriber_loc,
+ "only braces can be used for a macro transcriber "
+ "in declarative macro definition");
+ add_error (std::move (error));
+ return nullptr;
+ }
+
+ AST::MacroRule macro_rule
+ = AST::MacroRule (std::move (matcher), std::move (transcriber), locus);
+ std::vector<AST::MacroRule> macro_rules;
+ macro_rules.push_back (macro_rule);
+
+ return std::unique_ptr<AST::MacroRulesDefinition> (
+ AST::MacroRulesDefinition::decl_macro (std::move (rule_name),
+ macro_rules,
+ std::move (outer_attrs),
+ macro_locus, vis));
+ }
+ else if (t->get_id () == LEFT_CURLY)
+ {
+ // multiple definitions of macro rule separated by comma
+ // e.g. `macro foo { () => {}, ($e:expr) => {}, }`
+
+ // parse left curly
+ const_TokenPtr left_curly = expect_token (LEFT_CURLY);
+ if (left_curly == nullptr)
+ {
+ return nullptr;
+ }
+
+ // parse actual macro rules
+ std::vector<AST::MacroRule> macro_rules;
+
+ // must be at least one macro rule, so parse it
+ AST::MacroRule initial_rule = parse_macro_rule ();
+ if (initial_rule.is_error ())
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "required first macro rule in declarative macro definition "
+ "could not be parsed");
+ add_error (std::move (error));
+
+ // skip after somewhere?
+ return nullptr;
+ }
+ macro_rules.push_back (std::move (initial_rule));
+
+ t = lexer.peek_token ();
+ // parse macro rules
+ while (t->get_id () == COMMA)
+ {
+ // skip comma
+ lexer.skip_token ();
+
+ // don't parse if end of macro rules
+ if (token_id_matches_delims (lexer.peek_token ()->get_id (),
+ AST::CURLY))
+ {
+ break;
+ }
+
+ // try to parse next rule
+ AST::MacroRule rule = parse_macro_rule ();
+ if (rule.is_error ())
+ {
+ Error error (
+ lexer.peek_token ()->get_locus (),
+ "failed to parse macro rule in declarative macro definition");
+ add_error (std::move (error));
+
+ return nullptr;
+ }
+
+ macro_rules.push_back (std::move (rule));
+
+ t = lexer.peek_token ();
+ }
+
+ // parse right curly
+ const_TokenPtr right_curly = expect_token (RIGHT_CURLY);
+ if (right_curly == nullptr)
+ {
+ return nullptr;
+ }
+
+ return std::unique_ptr<AST::MacroRulesDefinition> (
+ AST::MacroRulesDefinition::decl_macro (std::move (rule_name),
+ std::move (macro_rules),
+ std::move (outer_attrs),
+ macro_locus, vis));
+ }
+ else
+ {
+ add_error (Error (t->get_locus (),
+ "unexpected token %qs - expecting delimiters "
+ "(for a declarative macro definiton)",
+ t->get_token_description ()));
+ return nullptr;
+ }
+}
+
// Parses a semi-coloned (except for full block) macro invocation item.
template <typename ManagedTokenSource>
std::unique_ptr<AST::MacroInvocation>
case STATIC_TOK:
case TRAIT:
case IMPL:
+ case MACRO:
/* TODO: implement union keyword but not really because of
* context-dependence crappy hack way to parse a union written below to
* separate it from the good code. */
case CRATE:
case DOLLAR_SIGN:
// almost certainly macro invocation semi
- return parse_macro_item (std::move (outer_attrs));
+ return parse_macro_invocation_semi (std::move (outer_attrs));
break;
// crappy hack to do union "keyword"
case IDENTIFIER:
else if (t->get_str () == "macro_rules")
{
// macro_rules! macro item
- return parse_macro_item (std::move (outer_attrs));
+ return parse_macro_rules_def (std::move (outer_attrs));
}
else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION
|| lexer.peek_token (1)->get_id () == EXCLAM)
// FIXME: ensure doesn't take any expressions by mistake
/* path (probably) or macro invocation, so probably a macro
* invocation semi */
- return parse_macro_item (std::move (outer_attrs));
+ return parse_macro_invocation_semi (std::move (outer_attrs));
}
gcc_fallthrough ();
// TODO: find out how to disable gcc "implicit fallthrough" warning
else if (t->get_str () == "macro_rules")
{
// macro_rules! macro item
- std::unique_ptr<AST::MacroItem> item (
- parse_macro_item (std::move (outer_attrs)));
+ std::unique_ptr<AST::Item> item (
+ parse_macro_rules_def (std::move (outer_attrs)));
return ExprOrStmt (std::move (item));
}
else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION