@@ -51,7 +51,6 @@ class Lifetime;
class GenericParam;
class LifetimeParam;
class ConstGenericParam;
-class MacroItem;
class TraitItem;
class InherentImplItem;
class TraitImplItem;
@@ -1284,6 +1284,7 @@ MacroRulesDefinition::as_string () const
// get outer attrs
str += append_attributes (outer_attrs, OUTER);
+ // TODO: deal with macro_2_0
str += "macro_rules!";
str += rule_name;
@@ -1350,11 +1350,6 @@ protected:
}
};
-// A macro item AST node - abstract base class
-class MacroItem : public Item
-{
-};
-
// Item used in trait declarations - abstract base class
class TraitItem
{
@@ -4391,8 +4391,6 @@ protected:
}
};
-// Replaced with forward decls - defined in "rust-macro.h"
-class MacroItem;
class MacroRulesDefinition;
} // namespace AST
} // namespace Rust
@@ -23,13 +23,11 @@
#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:
@@ -446,8 +444,18 @@ 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;
@@ -460,6 +468,7 @@ class MacroRulesDefinition : public MacroItem
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
@@ -479,27 +488,51 @@ class MacroRulesDefinition : public MacroItem
* 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.
@@ -549,7 +582,7 @@ protected:
* compile time */
class MacroInvocation : public TypeNoBounds,
public Pattern,
- public MacroItem,
+ public Item,
public TraitItem,
public TraitImplItem,
public InherentImplItem,
@@ -171,7 +171,7 @@ enum PrimitiveCoreType
RS_TOKEN_KEYWORD (IN, "in") \
RS_TOKEN_KEYWORD (LET, "let") \
RS_TOKEN_KEYWORD (LOOP, "loop") \
- RS_TOKEN_KEYWORD (MACRO, "macro") /* unused */ \
+ RS_TOKEN_KEYWORD (MACRO, "macro") \
RS_TOKEN_KEYWORD (MATCH_TOK, "match") \
RS_TOKEN_KEYWORD (MOD, "mod") \
RS_TOKEN_KEYWORD (MOVE, "move") \
@@ -1032,11 +1032,6 @@ Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
// 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 ())
@@ -1064,6 +1059,7 @@ Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
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. */
@@ -1078,7 +1074,7 @@ Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
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:
@@ -1092,19 +1088,18 @@ Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
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 (),
@@ -1335,6 +1330,8 @@ Parser<ManagedTokenSource>::parse_vis_item (AST::AttrVec outer_attrs)
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
@@ -1343,42 +1340,6 @@ Parser<ManagedTokenSource>::parse_vis_item (AST::AttrVec outer_attrs)
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>
@@ -1512,16 +1473,16 @@ Parser<ManagedTokenSource>::parse_macro_rules_def (AST::AttrVec outer_attrs)
{
// 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
{
@@ -1541,6 +1502,165 @@ Parser<ManagedTokenSource>::parse_macro_rules_def (AST::AttrVec outer_attrs)
}
}
+// 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>
@@ -6004,6 +6124,7 @@ Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
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. */
@@ -6019,7 +6140,7 @@ Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
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:
@@ -6032,7 +6153,7 @@ Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
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)
@@ -6040,7 +6161,7 @@ Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
// 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
@@ -11711,8 +11832,8 @@ Parser<ManagedTokenSource>::parse_stmt_or_expr_without_block ()
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
@@ -197,6 +197,8 @@ private:
AST::DelimTokenTree parse_delim_token_tree ();
std::unique_ptr<AST::MacroRulesDefinition>
parse_macro_rules_def (AST::AttrVec outer_attrs);
+ std::unique_ptr<AST::MacroRulesDefinition>
+ parse_decl_macro_def (AST::Visibility vis, AST::AttrVec outer_attrs);
std::unique_ptr<AST::MacroInvocation>
parse_macro_invocation_semi (AST::AttrVec outer_attrs);
std::unique_ptr<AST::MacroInvocation>
@@ -209,7 +211,6 @@ private:
// Top-level item-related
std::unique_ptr<AST::VisItem> parse_vis_item (AST::AttrVec outer_attrs);
- std::unique_ptr<AST::MacroItem> parse_macro_item (AST::AttrVec outer_attrs);
// VisItem subclass-related
std::unique_ptr<AST::Module> parse_module (AST::Visibility vis,
new file mode 100644
@@ -0,0 +1,2 @@
+#![feature(decl_macro)]
+macro m() {}
new file mode 100644
@@ -0,0 +1,2 @@
+#![feature(decl_macro)]
+pub macro m($e: expr) { $e + $e }
new file mode 100644
@@ -0,0 +1,4 @@
+#![feature(decl_macro)]
+macro foo {
+ () => {}
+}
new file mode 100644
@@ -0,0 +1,5 @@
+#![feature(decl_macro)]
+macro foo {
+ () => { 0 },
+ ($n: expr) => { $n + 1 },
+}
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,5 @@
+#![feature(decl_macro)]
+macro foo {
+ () => [],
+ ($h: expr, $(t: expr),*) => ($h),
+}
new file mode 100644
@@ -0,0 +1,5 @@
+#![feature(decl_macro)]
+macro m {}
+// { dg-error "unexpected token .\}. - expecting delimiters .for a macro matcher." "" { target *-*-* } .-1 }
+// { dg-error "required first macro rule in declarative macro definition could not be parsed" "" { target *-*-* } .-2 }
+// { dg-error "failed to parse item in crate" "" { target *-*-* } .-3 }
new file mode 100644
@@ -0,0 +1,4 @@
+#![feature(decl_macro)]
+pub macro hello() [ "Hello" ]
+// { dg-error "only braces can be used for a macro transcriber in declarative macro definition" "" { target *-*-* } .-1 }
+// { dg-error "failed to parse item in crate" }
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,8 @@
+#![feature(decl_macro)]
+macro one() {
+ 1
+}
+
+fn main() -> i32 {
+ one!() - 1
+}
new file mode 100644
@@ -0,0 +1,8 @@
+#![feature(decl_macro)]
+macro one {
+ () => { 1 }
+}
+
+fn main() -> i32 {
+ one!() - 1
+}
new file mode 100644
@@ -0,0 +1,15 @@
+#![feature(decl_macro)]
+macro add {
+ ($e:expr) => {
+ $e
+ },
+ ($h:expr, $($t:expr),*) => {
+ $h + add!($($t),*)
+ },
+}
+
+fn main() -> i32 {
+ let a = add!(1, 2, 3);
+
+ a - 6
+}
new file mode 100644
@@ -0,0 +1,15 @@
+#![feature(decl_macro)]
+pub macro add {
+ ($e:expr) => {
+ $e
+ },
+ ($h:expr, $($t:expr),*) => {
+ $h + add!($($t),*)
+ },
+}
+
+fn main() -> i32 {
+ let a = add!(1, 2, 3);
+
+ a - 6
+}