-
Notifications
You must be signed in to change notification settings - Fork 19
[WIP] Implement (possibly?) the entire Rust grammar. #13
Changes from 4 commits
c248bf1
7e8dfb6
4c7e6e9
6ad653f
9926a37
b267067
3ff30f8
1bf260f
7609557
e990067
b94e6be
178801e
91655dd
149f083
024e799
78f9302
b63b6e0
ebbc976
2bebbb7
686d5ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// FIXME(eddyb) implement more specific literal support in `gll::proc_macro` | ||
Abi = LITERAL?; | ||
|
||
/* | ||
// HACK(eddyb) taken from `librustc_target/spec/abi.rs` | ||
Abi = | ||
// Defaults to "C" | ||
{} | | ||
|
||
// Platform-specific ABIs | ||
"\"cdecl\"" | | ||
"\"stdcall\"" | | ||
"\"fastcall\"" | | ||
"\"vectorcall\"" | | ||
"\"thiscall\"" | | ||
"\"aapcs\"" | | ||
"\"win64\"" | | ||
"\"sysv64\"" | | ||
"\"ptx-kernel\"" | | ||
"\"msp430-interrupt\"" | | ||
"\"x86-interrupt\"" | | ||
"\"amdgpu-kernel\"" | | ||
|
||
// Cross-platform ABIs | ||
"\"Rust\"" | | ||
"\"C\"" | | ||
"\"system\"" | | ||
"\"rust-intrinsic\"" | | ||
"\"rust-call\"" | | ||
"\"platform-intrinsic\"" | | ||
"\"unadjusted\""; | ||
*/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,7 @@ | ||
OuterAttr = "#" attr:Attr; | ||
InnerAttr = "#!" attr:Attr; | ||
InnerAttr = "#" "!" attr:Attr; | ||
Attr = "[" path:Path input:AttrInput "]"; | ||
AttrInput = | ||
{} | | ||
"=" LITERAL | | ||
"(" TOKEN_TREE* ")" | | ||
"[" TOKEN_TREE* "]" | | ||
"{" TOKEN_TREE* "}"; | ||
"=" TOKEN_TREE | | ||
MacroInput; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
Expr = attrs:OuterAttr* kind:ExprKind; | ||
ExprKind = | ||
Literal:LITERAL | | ||
Paren:{ "(" attrs:InnerAttr* expr:Expr ")" } | | ||
Borrow:{ "&" mutable:"mut"? expr:Expr } | | ||
Box:{ "box" expr:Expr } | | ||
Unary:{ op:UnaryOp expr:Expr } | | ||
Try:{ expr:Expr "?" } | | ||
Binary:{ left:Expr op:BinaryOp right:Expr } | | ||
Assign:{ left:Expr { "=" | op:BinaryAssignOp } right:Expr } | | ||
Range:{ start:Expr? ".." end:Expr? } | | ||
RangeInclusive:{ start:Expr? "..=" end:Expr } | | ||
Type:{ expr:Expr ":" ty:Type } | | ||
Cast:{ expr:Expr "as" ty:Type } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Type without bounds. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Field:{ base:Expr "." field:FieldName } | | ||
Index:{ base:Expr "[" index:Expr "]" } | | ||
Array:{ "[" attrs:InnerAttr* exprs:Expr* % "," ","? "]" } | | ||
Repeat:{ "[" attrs:InnerAttr* elem:Expr ";" count:Expr "]" } | | ||
Tuple:{ "(" attrs:InnerAttr* exprs:Expr* % "," ","? ")" } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Same with types and patterns.) |
||
Path:QPath | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to me there needs to be more kinds of paths. For example, here it seems to allow |
||
Call:{ callee:Expr "(" args:Expr* % "," ","? ")" } | | ||
MethodCall:{ receiver:Expr "." method:PathSegment "(" args:Expr* % "," ","? ")" } | | ||
Struct:{ path:Path "{" attrs:InnerAttr* fields:StructExprFieldsAndBase "}" } | | ||
Block:{ { label:Label ":" }? unsafety:"unsafe"? block:Block } | | ||
AsyncBlock:{ "async" block:Block } | | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
TryBlock:{ "try" block:Block } | | ||
Continue:{ "continue" label:Label? } | | ||
Break:{ "break" label:Label? value:Expr? } | | ||
Return:{ "return" value:Expr? } | | ||
Yield:{ "yield" value:Expr? } | | ||
If:If | | ||
Match:{ "match" expr:Expr "{" attrs:InnerAttr* arms:MatchArm* "}" } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this Expr needs to exclude struct expressions. |
||
Loop:{ { label:Label ":" }? "loop" body:Block } | | ||
While:{ { label:Label ":" }? "while" cond:Cond body:Block } | | ||
For:{ { label:Label ":" }? "for" pat:Pat "in" expr:Expr body:Block } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this Expr needs to exclude struct expressions. |
||
Closure:{ | ||
generator_static:"static"? asyncness:"async"? by_val:"move"? | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"|" args:ClosureArg* % "," ","? "|" { "->" ret_ty:Type }? body:Expr | ||
} | | ||
MacroCall:MacroCall; | ||
|
||
UnaryOp = | ||
Not:"!" | | ||
Neg:"-" | | ||
Deref:"*"; | ||
|
||
BinaryOp = | ||
// Arithmetic & bitwise (these also have `BinaryAssignOp` forms) | ||
Add:"+" | | ||
Sub:"-" | | ||
Mul:"*" | | ||
Div:"/" | | ||
Rem:"%" | | ||
BitXor:"^" | | ||
BitAnd:"&" | | ||
BitOr:"|" | | ||
Shl:"<<" | | ||
Shr:">>" | | ||
|
||
// Logic (short-circuiting) | ||
LogicAnd:"&&" | | ||
LogicOr:"||" | | ||
|
||
// Comparisons | ||
Eq:"==" | | ||
Lt:"<" | | ||
Le:"<=" | | ||
Ne:"!=" | | ||
Gt:">" | | ||
Ge:">="; | ||
|
||
// FIXME(eddyb) figure out how to deduplicate this with `BinaryOp` | ||
// The problem is something like `BinaryOp "="` allows a space in between, | ||
// as the last token inside each `BinaryOp` case does not require being | ||
// joint, but each token before the `=` here does impose that requirement. | ||
BinaryAssignOp = | ||
Add:"+=" | | ||
Sub:"-=" | | ||
Mul:"*=" | | ||
Div:"/=" | | ||
Rem:"%=" | | ||
BitXor:"^=" | | ||
BitAnd:"&=" | | ||
BitOr:"|=" | | ||
Shl:"<<=" | | ||
Shr:">>="; | ||
|
||
FieldName = | ||
Ident:IDENT | | ||
// FIXME(eddyb) restrict this to only integer literals | ||
Numeric:LITERAL; | ||
|
||
// FIXME(eddyb) find a way to express this `A* B?` pattern better | ||
StructExprFieldsAndBase = | ||
Fields:{ fields:StructExprField* % "," ","? } | | ||
Base:{ ".." base:Expr } | | ||
FieldsAndBase:{ fields:StructExprField+ % "," "," ".." base:Expr }; | ||
|
||
StructExprField = attrs:OuterAttr* kind:StructExprFieldKind; | ||
StructExprFieldKind = | ||
Shorthand:IDENT | | ||
Explicit:{ field:FieldName ":" expr:Expr }; | ||
|
||
Label = LIFETIME; | ||
|
||
If = "if" cond:Cond then:Block { "else" else_expr:ElseExpr }?; | ||
Cond = | ||
Bool:Expr | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this (and the expr in "let" below) needs to exclude struct expressions. |
||
Let:{ "let" pat:Pat "=" expr:Expr }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That might've been accepted/implemented after I've already opened this PR, heh. |
||
ElseExpr = | ||
Block:Block | | ||
If:If; | ||
|
||
MatchArm = attrs:OuterAttr* "|"? pats:Pat+ % "|" { "if" guard:Expr }? "=>" body:Expr ","?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't seem to express the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I gave up on that when I realized |
||
|
||
ClosureArg = pat:Pat { ":" ty:Type }?; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
Generics = "<" params:GenericParam* % "," ","? ">"; | ||
GenericParam = attrs:OuterAttr* kind:GenericParamKind; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lifetimes can't be listed after types. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's more of a semantic restriction, to be really honest. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it actually were a semantic restriction then this would not be an error: macro_rules! foo {
($x: item) => {}
}
foo! {
fn stuff<T, 'a>() {}
} (Semantic vs. syntactic restrictions have semver consequences due to macros...) And yeah... grammar wise this is super annoying, especially since const generic variables can be interspersed with type variables (afaik, or we haven't made any decision here...). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we currently have the check duplicated (since the AST type loses the ordering requirement). @petrochenkov knows more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know anything anymore since @varkor rewrote everything for const generics. |
||
GenericParamKind = | ||
Lifetime:{ name:LIFETIME { ":" bounds:LifetimeBound* % "+" "+"? }? } | | ||
Type:{ name:IDENT { ":" bounds:TypeBound* % "+" "+"? }? { "=" default:Type }? }; | ||
|
||
ForAllBinder = "for" generics:Generics; | ||
|
||
WhereClause = "where" bounds:WhereBound* % "," ","?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This allows a bare There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, it's not valid! Hmm and neither is |
||
WhereBound = | ||
Lifetime:{ lt:LIFETIME ":" bounds:LifetimeBound* % "+" "+"? } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this allows a bare |
||
Type:{ binder:ForAllBinder? ty:Type ":" bounds:TypeBound* % "+" "+"? } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Disturbing fact: the following is valid Rust syntax:
however, syntactically these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yupp, we basically have a precedence rule that eagerly takes the first encountered Even more fun is the optional but not nestable parens around a bound. @Centril suggested we should make it a proper expression grammar with parens and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sorry, I tried to prevent that, but lang team forced the decision.
Why? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, it would explain the parens. And they do feel maybe like entities that can be arbitrarily composed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this allows a bare |
||
TypeEq:{ binder:ForAllBinder? left:Type { "=" | "==" } right:Type }; | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
LifetimeBound = outlives:LIFETIME; | ||
TypeBound = | ||
Outlives:LIFETIME | | ||
Trait:TypeTraitBound | | ||
TraitParen:{ "(" bound:TypeTraitBound ")" }; | ||
TypeTraitBound = unbound:"?"? binder:ForAllBinder? path:Path; | ||
|
||
GenericArgs = | ||
AngleBracket:{ "<" args_and_bindings:AngleBracketGenericArgsAndBindings? ","? ">" } | | ||
Paren:{ "(" inputs:Type* % "," ","? ")" { "->" output:Type }? }; | ||
|
||
// FIXME(eddyb) find a way to express this `A* B*` pattern better | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, this is tricky. I ended up listing each permutation which is a bit yuck. GenericArgs |
||
AngleBracketGenericArgsAndBindings = | ||
Args:GenericArg+ % "," | | ||
Bindings:TypeBinding+ % "," | | ||
ArgsAndBindings:{ args:GenericArg+ % "," "," bindings:TypeBinding+ % "," }; | ||
|
||
GenericArg = | ||
Lifetime:LIFETIME | | ||
Type:Type; | ||
TypeBinding = name:IDENT "=" ty:Type; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,92 @@ | ||
ModuleContents = attrs:InnerAttr* items:ItemWithOuterAttr*; | ||
ModuleContents = attrs:InnerAttr* items:Item*; | ||
|
||
ItemWithOuterAttr = attrs:OuterAttr* item:Item; | ||
// TODO other items | ||
Item = | ||
Item = attrs:OuterAttr* vis:Vis? kind:ItemKind; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As explained elsewhere, not special. |
||
ItemKind = | ||
Use:{ "use" use_tree:UseTree ";" } | | ||
ExternCrate:{ "extern" "crate" name:IDENT { "as" rename:IDENT }? ";" } | | ||
Use:{ "use" path:Path { "as" rename:IDENT }? ";" }; // TODO use trees | ||
Mod:{ "mod" name:IDENT { ";" | "{" contents:ModuleContents "}" } } | | ||
ForeignMod:{ "extern" abi:Abi "{" attrs:InnerAttr* items:ForeignItem* "}" } | | ||
Static:{ "static" mutable:"mut"? name:IDENT ":" ty:Type "=" value:Expr ";" } | | ||
Const:{ "const" name:IDENT ":" ty:Type "=" value:Expr ";" } | | ||
Fn:{ header:FnHeader "fn" decl:FnDecl body:Block } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably exclude variadics. |
||
TypeAlias:{ "type" name:IDENT generics:Generics? where_clause:WhereClause? "=" ty:Type ";" } | | ||
ExistentialType:{ "existential" "type" name:IDENT generics:Generics? where_clause:WhereClause? ":" bounds:TypeBound* % "+" "+"? ";" } | | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Enum:{ "enum" name:IDENT generics:Generics? where_clause:WhereClause? "{" variants:EnumVariant* % "," ","? "}" } | | ||
Struct:{ "struct" name:IDENT generics:Generics? body:StructBody } | | ||
Union:{ "union" name:IDENT generics:Generics? where_clause:WhereClause? "{" fields:RecordField* % "," ","? "}" } | | ||
Trait:{ | ||
unsafety:"unsafe"? auto:"auto"? "trait" name:IDENT generics:Generics? | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ ":" superbounds:TypeBound* % "+" "+"? }? | ||
where_clause:WhereClause? "{" trait_items:TraitItem* "}" | ||
} | | ||
TraitAlias:{ | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"trait" name:IDENT generics:Generics? | ||
{ ":" superbounds:TypeBound* % "+" "+"? }? | ||
where_clause:WhereClause? "=" bounds:TypeBound* % "+" "+"? ";" | ||
} | | ||
Impl:{ | ||
defaultness:"default"? unsafety:"unsafe"? "impl" generics:Generics? | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ negate:"!"? trait_path:Path "for" }? ty:Type | ||
where_clause:WhereClause? "{" attrs:InnerAttr* impl_items:ImplItem* "}" | ||
} | | ||
Macro:{ "macro" name:IDENT { "(" TOKEN_TREE* ")" }? "{" TOKEN_TREE* "}" } | | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
MacroCall:ItemMacroCall; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Macros cannot have visibility. |
||
|
||
UseTree = | ||
Glob:{ prefix:UseTreePrefix? "*" } | | ||
Nested:{ prefix:UseTreePrefix? "{" children:UseTree* % "," ","? "}" } | | ||
Simple:{ path:Path { "as" rename:IDENT }? }; | ||
UseTreePrefix = | ||
Path:{ path:Path "::" } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would need to be a mod-style path if you add more path types. |
||
Global:"::"; | ||
|
||
ForeignItem = attrs:OuterAttr* vis:Vis? kind:ForeignItemKind; | ||
ForeignItemKind = | ||
Static:{ "static" mutable:"mut"? name:IDENT ":" ty:Type ";" } | | ||
Fn:{ "fn" decl:FnDecl ";" } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems overly broad. Arguments must be named with a type, this allows anonymous arguments. |
||
Type:{ "type" name:IDENT ";" } | | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
MacroCall:ItemMacroCall; | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
TraitItem = attrs:OuterAttr* kind:TraitItemKind; | ||
TraitItemKind = | ||
Const:{ "const" name:IDENT ":" ty:Type { "=" default:Expr }? ";" } | | ||
Fn:{ header:FnHeader "fn" decl:FnDecl { default_body:Block | ";" } } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably exclude variadics. |
||
Type:{ "type" name:IDENT generics:Generics? { ":" bounds:TypeBound* % "+" "+"? }? where_clause:WhereClause? { "=" default:Type }? ";" } | | ||
MacroCall:ItemMacroCall; | ||
|
||
ImplItem = attrs:OuterAttr* defaultness:"default"? vis:Vis? kind:ImplItemKind; | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ImplItemKind = | ||
Const:{ "const" name:IDENT ":" ty:Type "=" value:Expr ";" } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A separate rule could be made for this so this long sequence isn't duplicated with the const item. |
||
Fn:{ header:FnHeader "fn" decl:FnDecl body:Block } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No variadics. |
||
Type:{ "type" name:IDENT generics:Generics? where_clause:WhereClause? "=" ty:Type ";" } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same note about sharing with TypeAlias. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The generics are GAT and therefore unstable, unlike in type aliases. |
||
ExistentialType:{ "existential" "type" name:IDENT generics:Generics? where_clause:WhereClause? ":" bounds:TypeBound* % "+" "+"? ";" } | | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
MacroCall:ItemMacroCall; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No vis on macro. |
||
|
||
FnHeader = constness:"const"? unsafety:"unsafe"? asyncness:"async"? { "extern" abi:Abi }?; | ||
FnDecl = name:IDENT generics:Generics? "(" args:FnArgs? ","? ")" { "->" ret_ty:Type }? where_clause:WhereClause?; | ||
|
||
// FIXME(eddyb) find a way to express this `A* B?` pattern better | ||
FnArgs = | ||
Regular:FnArg+ % "," | | ||
Variadic:"..." | | ||
RegularAndVariadic:{ args:FnArg+ % "," "," "..." }; | ||
FnArg = | ||
SelfValue:{ mutable:"mut"? "self" } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This misses type ascription I think (which is stable in limited form)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that that parses, but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Centril do we yet have a proposal for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @eddyb that would just fall out from having pat ::= "self" | pat "@" pat | pat ":" type | ... ; I've mentioned the possibility of making |
||
SelfRef:{ "&" lt:LIFETIME? mutable:"mut"? "self" } | | ||
Regular:FnSigInput; | ||
|
||
EnumVariant = attrs:OuterAttr* name:IDENT kind:EnumVariantKind { "=" discr:Expr }?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the discriminant cannot appear next to tuple or record-style variants. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should put a FIXME there. I think I hit some strange bug when I tried "doing it right". |
||
EnumVariantKind = | ||
Unit:{} | | ||
Tuple:{ "(" fields:TupleField* % "," ","? ")" } | | ||
Record:{ "{" fields:RecordField* % "," ","? "}" }; | ||
|
||
// FIXME(eddyb) could maybe be shared more with `EnumVariantKind`? | ||
// The problem is the semicolons for `Unit` and `Tuple`, and the where clauses. | ||
StructBody = | ||
Unit:{ where_clause:WhereClause? ";" } | | ||
Tuple:{ "(" fields:TupleField* % "," ","? ")" where_clause:WhereClause? ";" } | | ||
Record:{ where_clause:WhereClause? "{" fields:RecordField* % "," ","? "}" }; | ||
|
||
TupleField = attrs:OuterAttr* vis:Vis? ty:Type; | ||
RecordField = attrs:OuterAttr* vis:Vis? name:IDENT ":" ty:Type; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
MacroCall = path:Path "!" ident_input:IDENT? input:MacroInput; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When I was studying the parser code, this IDENT here had me stumped. Is this some old vestigial thing? I can't find a way to get it to actually parse, though. Also, the type of path depends on where it is used. For the reference grammar, I just said it was a simple ("mod"-style) path, because in practice that's the only way it can be used. But from a parsing standpoint, I guess it could be more accurate. For example, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. macro_rules! name {
// ....
} It depends on whether There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, interesting. What I was thinking of is that foo!bar{} is considered a valid statement in libsyntax (/~https://github.com/rust-lang/rust/blob/65e485d8f1d28102b426c9d6d82f835cd6470d3e/src/libsyntax/parse/parser.rs#L4711-L4714). I assumed that was what it was referring to. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Parsing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I'm understanding it a little better. However, macro calls are parsed differently in various situations, and some don't allow the extra ident (type, pattern, trait/impl item, foreign item). Eventually these differences will need to be captured in the grammar, right? Also, I think the syntax for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We could do this, especially since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ideally we would find a way to keep adding rules without making the grammar unreadable and without losing the ability to encode into (E)BNF (automatically). |
||
MacroInput = | ||
"(" TOKEN_TREE* ")" | | ||
"[" TOKEN_TREE* "]" | | ||
"{" TOKEN_TREE* "}"; | ||
|
||
// FIXME(eddyb) could maybe be shared more with `MacroInput`? | ||
// The problem is the semicolons for the `()` and `[]` cases. | ||
ItemMacroCall = path:Path "!" ident_input:IDENT? input:ItemMacroInput; | ||
ItemMacroInput = | ||
"(" TOKEN_TREE* ")" ";" | | ||
"[" TOKEN_TREE* "]" ";" | | ||
"{" TOKEN_TREE* "}"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
Pat = | ||
Wild:"_" | | ||
Literal:{ minus:"-"? lit:LITERAL } | | ||
Range:{ start:PatRangeValue ".." end:PatRangeValue } | | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
RangeInclusive:{ start:PatRangeValue { "..." | "..=" } end:PatRangeValue } | | ||
Binding:{ binding:Binding { "@" subpat:Pat }? } | | ||
Paren:{ "(" pat:Pat ")" } | | ||
Ref:{ "&" mutable:"mut"? pat:Pat } | | ||
Box:{ "box" pat:Pat } | | ||
Centril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Slice:{ "[" elems:SlicePatElem* % "," ","? "]" } | | ||
Tuple:{ "(" fields:TuplePatField* % "," ","? ")" } | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does gll match in order? Like here, does Also, trailing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GLL matches every possible option. This initial draft of the grammar lacks any disambiguation. |
||
Path:QPath | | ||
TupleStruct:{ path:Path "(" fields:TuplePatField* % "," ","? ")" } | | ||
Struct:{ path:Path "{" fields:StructPatFieldsAndEllipsis "}" } | | ||
MacroCall:MacroCall; | ||
|
||
PatRangeValue = | ||
Literal:{ minus:"-"? lit:LITERAL } | | ||
Path:QPath; | ||
|
||
Binding = boxed:"box"? reference:"ref"? mutable:"mut"? name:IDENT; | ||
|
||
SlicePatElem = | ||
Subslice:{ subpat:Pat? ".." } | | ||
Pat:Pat; | ||
|
||
TuplePatField = | ||
Ellipsis:".." | | ||
Pat:Pat; | ||
|
||
// FIXME(eddyb) find a way to express this `A* B?` pattern better | ||
StructPatFieldsAndEllipsis = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Individual fields (including There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is being tested! Can you remove it from the official parser and show that it breaks no tests? And then add tests. I can also do it if you want. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like it is covered by |
||
Fields:{ fields:StructPatField* % "," ","? } | | ||
Ellipsis:{ ".." } | | ||
FieldsAndEllipsis:{ fields:StructPatField+ % "," "," ".." }; | ||
|
||
StructPatField = | ||
Shorthand:Binding | | ||
Explicit:{ field:FieldName ":" pat:Pat }; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,8 @@ | ||
Path = global:"::"? segments:PathSegment* % "::"; | ||
PathSegment = ident:IDENT; // TODO generics | ||
Path = global:"::"? path:RelativePath; | ||
RelativePath = segments:PathSegment+ % "::"; | ||
PathSegment = ident:IDENT { "::"? args:GenericArgs }?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That should parse as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should write this down somewhere less ephemeral ;) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Less ephemeral - rust-lang/rust#55640. |
||
|
||
QSelf = "<" ty:Type { "as" trait_path:Path }? ">"; | ||
QPath = | ||
Unqualified:Path | | ||
Qualified:{ qself:QSelf "::" path:RelativePath }; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Stmt = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It may be useful to have a separate entry for macro call here. It would need to be like ItemMacroCall, and maybe call it something different (since it is not necessarily an item). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's also included as an expression, though? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, yea, assuming expression is fixed to deal with |
||
Item:Item | | ||
Local:{ attrs:OuterAttr* "let" pat:Pat { ":" ty:Type }? { "=" init:Expr }? ";" } | | ||
Expr:Expr | | ||
Semi:";"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If nonterminals in this grammar are supposed to match fragments in
|
||
|
||
Block = "{" attrs:InnerAttr* Stmt* "}"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't seem to handle |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using token_tree (and macroinput) here seems very broad. The grammar at /~https://github.com/rust-lang-nursery/reference/blob/master/src/attributes.md I think is correct and more specific. I'm not sure what would be a good way to express that literals cannot have suffixes, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ehuss This is intended afaik. rust-lang/rust#55208.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, indeed, I had forgotten about that. Regardless, I think it would be good if the grammar eventually covered the stable syntax.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK it has been stabilized for attribute proc macros, just not built-in ones?
@petrochenkov can you confirm?
As for specific built-in attributes, sure, we could provide grammars for each of them, but it's similar to having a grammar for e.g.
format_args!
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Syntax with delimiters is stable and is rejected/gated in non-macro attributes with a semantic check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PATH = TOKEN_TREE
is mostly unstable, I think.Key-value attributes are not allowed in macros, and gated for non-literals in non-macros, but gating happens after expansion, so something like
#[doc = $doc]
where$doc: expr
is also on stable.