mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 07:04:22 +00:00
Parse builtin#asm expressions
This commit is contained in:
parent
50882fbfa2
commit
86658c66b4
21 changed files with 865 additions and 31 deletions
|
@ -15,6 +15,7 @@ extend-ignore-re = [
|
|||
'"flate2"',
|
||||
"raison d'être",
|
||||
"inout",
|
||||
"INOUT",
|
||||
"optin"
|
||||
]
|
||||
|
||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2624,6 +2624,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"directories",
|
||||
"either",
|
||||
"flate2",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
|
|
|
@ -185,6 +185,7 @@ style = { level = "warn", priority = -1 }
|
|||
suspicious = { level = "warn", priority = -1 }
|
||||
|
||||
## allow following lints
|
||||
too_long_first_doc_paragraph = "allow"
|
||||
# subjective
|
||||
single_match = "allow"
|
||||
# () makes a fine error in most cases
|
||||
|
|
|
@ -694,8 +694,11 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
|
||||
ast::Expr::AsmExpr(e) => {
|
||||
let e = self.collect_expr_opt(e.expr());
|
||||
self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr)
|
||||
let template = e.template().map(|it| self.collect_expr(it)).collect();
|
||||
self.alloc_expr(
|
||||
Expr::InlineAsm(InlineAsm { template, operands: Box::default() }),
|
||||
syntax_ptr,
|
||||
)
|
||||
}
|
||||
ast::Expr::OffsetOfExpr(e) => {
|
||||
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
|
||||
|
|
|
@ -307,7 +307,8 @@ pub struct OffsetOf {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InlineAsm {
|
||||
pub e: ExprId,
|
||||
pub template: Box<[ExprId]>,
|
||||
pub operands: Box<[()]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -372,7 +373,7 @@ impl Expr {
|
|||
match self {
|
||||
Expr::Missing => {}
|
||||
Expr::Path(_) | Expr::OffsetOf(_) => {}
|
||||
Expr::InlineAsm(it) => f(it.e),
|
||||
Expr::InlineAsm(it) => it.template.iter().copied().for_each(f),
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
f(*condition);
|
||||
f(*then_branch);
|
||||
|
|
|
@ -666,7 +666,9 @@ impl InferenceContext<'_> {
|
|||
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
|
||||
match &self.body[tgt_expr] {
|
||||
Expr::OffsetOf(_) => (),
|
||||
Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e),
|
||||
Expr::InlineAsm(e) => {
|
||||
e.template.iter().for_each(|it| self.walk_expr_without_adjust(*it))
|
||||
}
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
self.consume_expr(*condition);
|
||||
self.consume_expr(*then_branch);
|
||||
|
|
|
@ -925,7 +925,7 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
|
||||
Expr::InlineAsm(it) => {
|
||||
self.infer_expr_no_expect(it.e);
|
||||
it.template.iter().for_each(|&expr| _ = self.infer_expr_no_expect(expr));
|
||||
self.result.standard_types.unit.clone()
|
||||
}
|
||||
};
|
||||
|
|
|
@ -39,7 +39,10 @@ impl InferenceContext<'_> {
|
|||
fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
|
||||
match &self.body[tgt_expr] {
|
||||
Expr::Missing => (),
|
||||
Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not),
|
||||
Expr::InlineAsm(e) => e
|
||||
.template
|
||||
.iter()
|
||||
.for_each(|&expr| self.infer_mut_expr_without_adjust(expr, Mutability::Not)),
|
||||
Expr::OffsetOf(_) => (),
|
||||
&Expr::If { condition, then_branch, else_branch } => {
|
||||
self.infer_mut_expr(condition, Mutability::Not);
|
||||
|
@ -129,7 +132,7 @@ impl InferenceContext<'_> {
|
|||
target,
|
||||
}) = base_adjustments
|
||||
{
|
||||
// For assignee exprs `IndexMut` obiligations are already applied
|
||||
// For assignee exprs `IndexMut` obligations are already applied
|
||||
if !is_assignee_expr {
|
||||
if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
|
||||
base_ty = Some(ty.clone());
|
||||
|
|
|
@ -245,7 +245,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
|
|||
|
||||
// test builtin_expr
|
||||
// fn foo() {
|
||||
// builtin#asm(0);
|
||||
// builtin#asm("");
|
||||
// builtin#format_args("", 0, 1, a = 2 + 3, a + b);
|
||||
// builtin#offset_of(Foo, bar.baz.0);
|
||||
// }
|
||||
|
@ -297,18 +297,175 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
|
|||
p.expect(T![')']);
|
||||
Some(m.complete(p, FORMAT_ARGS_EXPR))
|
||||
} else if p.at_contextual_kw(T![asm]) {
|
||||
p.bump_remap(T![asm]);
|
||||
p.expect(T!['(']);
|
||||
// FIXME: We just put expression here so highlighting kind of keeps working
|
||||
expr(p);
|
||||
p.expect(T![')']);
|
||||
Some(m.complete(p, ASM_EXPR))
|
||||
parse_asm_expr(p, m)
|
||||
} else {
|
||||
m.abandon(p);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// test asm_expr
|
||||
// fn foo() {
|
||||
// builtin#asm(
|
||||
// "mov {tmp}, {x}",
|
||||
// "shl {tmp}, 1",
|
||||
// "shl {x}, 2",
|
||||
// "add {x}, {tmp}",
|
||||
// x = inout(reg) x,
|
||||
// tmp = out(reg) _,
|
||||
// );
|
||||
// }
|
||||
fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
|
||||
p.bump_remap(T![asm]);
|
||||
p.expect(T!['(']);
|
||||
if expr(p).is_none() {
|
||||
p.err_and_bump("expected asm template");
|
||||
}
|
||||
let mut allow_templates = true;
|
||||
while !p.at(EOF) && !p.at(T![')']) {
|
||||
p.expect(T![,]);
|
||||
// accept trailing commas
|
||||
if p.at(T![')']) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse clobber_abi
|
||||
if p.eat_contextual_kw(T![clobber_abi]) {
|
||||
parse_clobber_abi(p);
|
||||
allow_templates = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse options
|
||||
if p.eat_contextual_kw(T![options]) {
|
||||
parse_options(p);
|
||||
allow_templates = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse operand names
|
||||
if p.at(T![ident]) && p.nth_at(1, T![=]) {
|
||||
name(p);
|
||||
p.bump(T![=]);
|
||||
allow_templates = false;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let op = p.start();
|
||||
if p.eat(T![in]) {
|
||||
parse_reg(p);
|
||||
expr(p);
|
||||
op.complete(p, ASM_REG_OPERAND);
|
||||
} else if p.eat_contextual_kw(T![out]) {
|
||||
parse_reg(p);
|
||||
expr(p);
|
||||
op.complete(p, ASM_REG_OPERAND);
|
||||
} else if p.eat_contextual_kw(T![lateout]) {
|
||||
parse_reg(p);
|
||||
expr(p);
|
||||
op.complete(p, ASM_REG_OPERAND);
|
||||
} else if p.eat_contextual_kw(T![inout]) {
|
||||
parse_reg(p);
|
||||
expr(p);
|
||||
if p.eat(T![=>]) {
|
||||
expr(p);
|
||||
}
|
||||
op.complete(p, ASM_REG_OPERAND);
|
||||
} else if p.eat_contextual_kw(T![inlateout]) {
|
||||
parse_reg(p);
|
||||
expr(p);
|
||||
if p.eat(T![=>]) {
|
||||
expr(p);
|
||||
}
|
||||
op.complete(p, ASM_REG_OPERAND);
|
||||
} else if p.eat_contextual_kw(T![label]) {
|
||||
block_expr(p);
|
||||
op.complete(p, ASM_LABEL);
|
||||
} else if p.eat(T![const]) {
|
||||
expr(p);
|
||||
op.complete(p, ASM_CONST);
|
||||
} else if p.eat_contextual_kw(T![sym]) {
|
||||
expr(p);
|
||||
op.complete(p, ASM_SYM);
|
||||
} else if allow_templates {
|
||||
op.abandon(p);
|
||||
if expr(p).is_none() {
|
||||
p.err_and_bump("expected asm template");
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
op.abandon(p);
|
||||
p.err_and_bump("expected asm operand");
|
||||
if p.at(T!['}']) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
};
|
||||
allow_templates = false;
|
||||
}
|
||||
p.expect(T![')']);
|
||||
Some(m.complete(p, ASM_EXPR))
|
||||
}
|
||||
|
||||
fn parse_options(p: &mut Parser<'_>) {
|
||||
p.expect(T!['(']);
|
||||
|
||||
while !p.eat(T![')']) && !p.at(EOF) {
|
||||
const OPTIONS: &[SyntaxKind] = &[
|
||||
T![pure],
|
||||
T![nomem],
|
||||
T![readonly],
|
||||
T![preserves_flags],
|
||||
T![noreturn],
|
||||
T![nostack],
|
||||
T![may_unwind],
|
||||
T![att_syntax],
|
||||
T![raw],
|
||||
];
|
||||
|
||||
if !OPTIONS.iter().any(|&syntax| p.eat(syntax)) {
|
||||
p.err_and_bump("expected asm option");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allow trailing commas
|
||||
if p.eat(T![')']) {
|
||||
break;
|
||||
}
|
||||
p.expect(T![,]);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_clobber_abi(p: &mut Parser<'_>) {
|
||||
p.expect(T!['(']);
|
||||
|
||||
while !p.eat(T![')']) && !p.at(EOF) {
|
||||
if !p.expect(T![string]) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Allow trailing commas
|
||||
if p.eat(T![')']) {
|
||||
break;
|
||||
}
|
||||
p.expect(T![,]);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_reg(p: &mut Parser<'_>) {
|
||||
p.expect(T!['(']);
|
||||
if p.at(T![ident]) {
|
||||
name_ref(p)
|
||||
} else if p.at(T![string]) {
|
||||
p.bump_any()
|
||||
} else {
|
||||
p.err_and_bump("expected register name");
|
||||
}
|
||||
p.expect(T![')']);
|
||||
}
|
||||
|
||||
// test array_expr
|
||||
// fn foo() {
|
||||
// [];
|
||||
|
|
|
@ -131,6 +131,14 @@ impl<'t> Parser<'t> {
|
|||
true
|
||||
}
|
||||
|
||||
pub(crate) fn eat_contextual_kw(&mut self, kind: SyntaxKind) -> bool {
|
||||
if !self.at_contextual_kw(kind) {
|
||||
return false;
|
||||
}
|
||||
self.bump_remap(kind);
|
||||
true
|
||||
}
|
||||
|
||||
fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
|
||||
self.inp.kind(self.pos + n) == k1
|
||||
&& self.inp.kind(self.pos + n + 1) == k2
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -19,6 +19,8 @@ mod ok {
|
|||
#[test]
|
||||
fn as_precedence() { run_and_expect_no_errors("test_data/parser/inline/ok/as_precedence.rs"); }
|
||||
#[test]
|
||||
fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); }
|
||||
#[test]
|
||||
fn assoc_const_eq() {
|
||||
run_and_expect_no_errors("test_data/parser/inline/ok/assoc_const_eq.rs");
|
||||
}
|
||||
|
|
77
crates/parser/test_data/parser/inline/ok/asm_expr.rast
Normal file
77
crates/parser/test_data/parser/inline/ok/asm_expr.rast
Normal file
|
@ -0,0 +1,77 @@
|
|||
SOURCE_FILE
|
||||
FN
|
||||
FN_KW "fn"
|
||||
WHITESPACE " "
|
||||
NAME
|
||||
IDENT "foo"
|
||||
PARAM_LIST
|
||||
L_PAREN "("
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
BLOCK_EXPR
|
||||
STMT_LIST
|
||||
L_CURLY "{"
|
||||
WHITESPACE "\n "
|
||||
EXPR_STMT
|
||||
ASM_EXPR
|
||||
BUILTIN_KW "builtin"
|
||||
POUND "#"
|
||||
ASM_KW "asm"
|
||||
L_PAREN "("
|
||||
WHITESPACE "\n "
|
||||
LITERAL
|
||||
STRING "\"mov {tmp}, {x}\""
|
||||
COMMA ","
|
||||
WHITESPACE "\n "
|
||||
LITERAL
|
||||
STRING "\"shl {tmp}, 1\""
|
||||
COMMA ","
|
||||
WHITESPACE "\n "
|
||||
LITERAL
|
||||
STRING "\"shl {x}, 2\""
|
||||
COMMA ","
|
||||
WHITESPACE "\n "
|
||||
LITERAL
|
||||
STRING "\"add {x}, {tmp}\""
|
||||
COMMA ","
|
||||
WHITESPACE "\n "
|
||||
NAME
|
||||
IDENT "x"
|
||||
WHITESPACE " "
|
||||
EQ "="
|
||||
WHITESPACE " "
|
||||
ASM_REG_OPERAND
|
||||
INOUT_KW "inout"
|
||||
L_PAREN "("
|
||||
NAME_REF
|
||||
IDENT "reg"
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
PATH_EXPR
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "x"
|
||||
COMMA ","
|
||||
WHITESPACE "\n "
|
||||
NAME
|
||||
IDENT "tmp"
|
||||
WHITESPACE " "
|
||||
EQ "="
|
||||
WHITESPACE " "
|
||||
ASM_REG_OPERAND
|
||||
OUT_KW "out"
|
||||
L_PAREN "("
|
||||
NAME_REF
|
||||
IDENT "reg"
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
UNDERSCORE_EXPR
|
||||
UNDERSCORE "_"
|
||||
COMMA ","
|
||||
WHITESPACE "\n "
|
||||
R_PAREN ")"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n"
|
||||
R_CURLY "}"
|
||||
WHITESPACE "\n"
|
10
crates/parser/test_data/parser/inline/ok/asm_expr.rs
Normal file
10
crates/parser/test_data/parser/inline/ok/asm_expr.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
fn foo() {
|
||||
builtin#asm(
|
||||
"mov {tmp}, {x}",
|
||||
"shl {tmp}, 1",
|
||||
"shl {x}, 2",
|
||||
"add {x}, {tmp}",
|
||||
x = inout(reg) x,
|
||||
tmp = out(reg) _,
|
||||
);
|
||||
}
|
|
@ -19,7 +19,7 @@ SOURCE_FILE
|
|||
ASM_KW "asm"
|
||||
L_PAREN "("
|
||||
LITERAL
|
||||
INT_NUMBER "0"
|
||||
STRING "\"\""
|
||||
R_PAREN ")"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n "
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
fn foo() {
|
||||
builtin#asm(0);
|
||||
builtin#asm("");
|
||||
builtin#format_args("", 0, 1, a = 2 + 3, a + b);
|
||||
builtin#offset_of(Foo, bar.baz.0);
|
||||
}
|
||||
|
|
|
@ -391,8 +391,33 @@ Expr =
|
|||
OffsetOfExpr =
|
||||
Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')'
|
||||
|
||||
// asm := "asm!(" format_string *("," format_string) *("," operand) [","] ")"
|
||||
// global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")"
|
||||
// format_string := STRING_LITERAL / RAW_STRING_LITERAL
|
||||
AsmExpr =
|
||||
Attr* 'builtin' '#' 'asm' '(' Expr ')'
|
||||
Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmOperand (',' AsmOperand)*)? ','? ')'
|
||||
|
||||
// operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
|
||||
AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)?
|
||||
// dir_spec := "in" / "out" / "lateout" / "inout" / "inlateout"
|
||||
AsmDirSpec = 'in' | 'out' | 'lateout' | 'inout' | 'inlateout'
|
||||
// reg_spec := <register class> / "\"" <explicit register> "\""
|
||||
AsmRegSpec = '@string' | NameRef
|
||||
// reg_operand := [ident "="] dir_spec "(" reg_spec ")" operand_expr
|
||||
AsmRegOperand = (Name '=')? AsmDirSpec '(' AsmRegSpec ')' AsmOperandExpr
|
||||
// clobber_abi := "clobber_abi(" <abi> *("," <abi>) [","] ")"
|
||||
AsmClobberAbi = 'clobber_abi' '(' ('@string' (',' '@string')* ','?) ')'
|
||||
// option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw"
|
||||
AsmOption = 'pure' | 'nomem' | 'readonly' | 'preserves_flags' | 'noreturn' | 'nostack' | 'att_syntax' | 'raw' | 'may_unwind'
|
||||
// options := "options(" option *("," option) [","] ")"
|
||||
AsmOptions = 'options' '(' AsmOption *(',' AsmOption) ','? ')'
|
||||
// operand := reg_operand / clobber_abi / options
|
||||
AsmOperand = AsmRegOperand | AsmClobberAbi | AsmOptions | AsmLabel
|
||||
AsmLabel = 'label' BlockExpr
|
||||
AsmSym = 'sym' Expr
|
||||
AsmConst = 'const' Expr
|
||||
|
||||
|
||||
|
||||
FormatArgsExpr =
|
||||
Attr* 'builtin' '#' 'format_args' '('
|
||||
|
|
|
@ -64,6 +64,53 @@ impl ArrayType {
|
|||
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AsmClobberAbi {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl AsmClobberAbi {
|
||||
#[inline]
|
||||
pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
|
||||
#[inline]
|
||||
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
|
||||
#[inline]
|
||||
pub fn clobber_abi_token(&self) -> Option<SyntaxToken> {
|
||||
support::token(&self.syntax, T![clobber_abi])
|
||||
}
|
||||
#[inline]
|
||||
pub fn string_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![string]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AsmConst {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl AsmConst {
|
||||
#[inline]
|
||||
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AsmDirSpec {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl AsmDirSpec {
|
||||
#[inline]
|
||||
pub fn in_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![in]) }
|
||||
#[inline]
|
||||
pub fn inlateout_token(&self) -> Option<SyntaxToken> {
|
||||
support::token(&self.syntax, T![inlateout])
|
||||
}
|
||||
#[inline]
|
||||
pub fn inout_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![inout]) }
|
||||
#[inline]
|
||||
pub fn lateout_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![lateout]) }
|
||||
#[inline]
|
||||
pub fn out_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![out]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AsmExpr {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
|
@ -71,7 +118,9 @@ pub struct AsmExpr {
|
|||
impl ast::HasAttrs for AsmExpr {}
|
||||
impl AsmExpr {
|
||||
#[inline]
|
||||
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
|
||||
pub fn asm_operands(&self) -> AstChildren<AsmOperand> { support::children(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn template(&self) -> AstChildren<Expr> { support::children(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
|
||||
#[inline]
|
||||
|
@ -79,11 +128,133 @@ impl AsmExpr {
|
|||
#[inline]
|
||||
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
|
||||
#[inline]
|
||||
pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
|
||||
#[inline]
|
||||
pub fn asm_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![asm]) }
|
||||
#[inline]
|
||||
pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AsmLabel {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl AsmLabel {
|
||||
#[inline]
|
||||
pub fn block_expr(&self) -> Option<BlockExpr> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn label_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![label]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AsmOperandExpr {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl AsmOperandExpr {
|
||||
#[inline]
|
||||
pub fn in_expr(&self) -> Option<Expr> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn out_expr(&self) -> Option<Expr> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn fat_arrow_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=>]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AsmOption {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl AsmOption {
|
||||
#[inline]
|
||||
pub fn att_syntax_token(&self) -> Option<SyntaxToken> {
|
||||
support::token(&self.syntax, T![att_syntax])
|
||||
}
|
||||
#[inline]
|
||||
pub fn may_unwind_token(&self) -> Option<SyntaxToken> {
|
||||
support::token(&self.syntax, T![may_unwind])
|
||||
}
|
||||
#[inline]
|
||||
pub fn nomem_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![nomem]) }
|
||||
#[inline]
|
||||
pub fn noreturn_token(&self) -> Option<SyntaxToken> {
|
||||
support::token(&self.syntax, T![noreturn])
|
||||
}
|
||||
#[inline]
|
||||
pub fn nostack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![nostack]) }
|
||||
#[inline]
|
||||
pub fn preserves_flags_token(&self) -> Option<SyntaxToken> {
|
||||
support::token(&self.syntax, T![preserves_flags])
|
||||
}
|
||||
#[inline]
|
||||
pub fn pure_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![pure]) }
|
||||
#[inline]
|
||||
pub fn raw_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![raw]) }
|
||||
#[inline]
|
||||
pub fn readonly_token(&self) -> Option<SyntaxToken> {
|
||||
support::token(&self.syntax, T![readonly])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AsmOptions {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl AsmOptions {
|
||||
#[inline]
|
||||
pub fn asm_option(&self) -> Option<AsmOption> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn asm_options(&self) -> AstChildren<AsmOption> { support::children(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
|
||||
#[inline]
|
||||
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
|
||||
#[inline]
|
||||
pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
|
||||
#[inline]
|
||||
pub fn options_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![options]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AsmRegOperand {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl ast::HasName for AsmRegOperand {}
|
||||
impl AsmRegOperand {
|
||||
#[inline]
|
||||
pub fn asm_dir_spec(&self) -> Option<AsmDirSpec> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn asm_operand_expr(&self) -> Option<AsmOperandExpr> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn asm_reg_spec(&self) -> Option<AsmRegSpec> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
|
||||
#[inline]
|
||||
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
|
||||
#[inline]
|
||||
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AsmRegSpec {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl AsmRegSpec {
|
||||
#[inline]
|
||||
pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn string_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![string]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AsmSym {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl AsmSym {
|
||||
#[inline]
|
||||
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn sym_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![sym]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AssocItemList {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
|
@ -2051,6 +2222,14 @@ impl ast::HasGenericParams for Adt {}
|
|||
impl ast::HasName for Adt {}
|
||||
impl ast::HasVisibility for Adt {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum AsmOperand {
|
||||
AsmClobberAbi(AsmClobberAbi),
|
||||
AsmLabel(AsmLabel),
|
||||
AsmOptions(AsmOptions),
|
||||
AsmRegOperand(AsmRegOperand),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum AssocItem {
|
||||
Const(Const),
|
||||
|
@ -2316,6 +2495,48 @@ impl AstNode for ArrayType {
|
|||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AsmClobberAbi {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CLOBBER_ABI }
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AsmConst {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CONST }
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AsmDirSpec {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_DIR_SPEC }
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AsmExpr {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR }
|
||||
|
@ -2330,6 +2551,104 @@ impl AstNode for AsmExpr {
|
|||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AsmLabel {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_LABEL }
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AsmOperandExpr {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_EXPR }
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AsmOption {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTION }
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AsmOptions {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTIONS }
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AsmRegOperand {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_OPERAND }
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AsmRegSpec {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_SPEC }
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AsmSym {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_SYM }
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for AssocItemList {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_ITEM_LIST }
|
||||
|
@ -4268,6 +4587,48 @@ impl AstNode for Adt {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl From<AsmClobberAbi> for AsmOperand {
|
||||
#[inline]
|
||||
fn from(node: AsmClobberAbi) -> AsmOperand { AsmOperand::AsmClobberAbi(node) }
|
||||
}
|
||||
impl From<AsmLabel> for AsmOperand {
|
||||
#[inline]
|
||||
fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) }
|
||||
}
|
||||
impl From<AsmOptions> for AsmOperand {
|
||||
#[inline]
|
||||
fn from(node: AsmOptions) -> AsmOperand { AsmOperand::AsmOptions(node) }
|
||||
}
|
||||
impl From<AsmRegOperand> for AsmOperand {
|
||||
#[inline]
|
||||
fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) }
|
||||
}
|
||||
impl AstNode for AsmOperand {
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool {
|
||||
matches!(kind, ASM_CLOBBER_ABI | ASM_LABEL | ASM_OPTIONS | ASM_REG_OPERAND)
|
||||
}
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
let res = match syntax.kind() {
|
||||
ASM_CLOBBER_ABI => AsmOperand::AsmClobberAbi(AsmClobberAbi { syntax }),
|
||||
ASM_LABEL => AsmOperand::AsmLabel(AsmLabel { syntax }),
|
||||
ASM_OPTIONS => AsmOperand::AsmOptions(AsmOptions { syntax }),
|
||||
ASM_REG_OPERAND => AsmOperand::AsmRegOperand(AsmRegOperand { syntax }),
|
||||
_ => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode {
|
||||
match self {
|
||||
AsmOperand::AsmClobberAbi(it) => &it.syntax,
|
||||
AsmOperand::AsmLabel(it) => &it.syntax,
|
||||
AsmOperand::AsmOptions(it) => &it.syntax,
|
||||
AsmOperand::AsmRegOperand(it) => &it.syntax,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<Const> for AssocItem {
|
||||
#[inline]
|
||||
fn from(node: Const) -> AssocItem { AssocItem::Const(node) }
|
||||
|
@ -5803,7 +6164,8 @@ impl AstNode for AnyHasName {
|
|||
fn can_cast(kind: SyntaxKind) -> bool {
|
||||
matches!(
|
||||
kind,
|
||||
CONST
|
||||
ASM_REG_OPERAND
|
||||
| CONST
|
||||
| CONST_PARAM
|
||||
| ENUM
|
||||
| FN
|
||||
|
@ -5832,6 +6194,10 @@ impl AstNode for AnyHasName {
|
|||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl From<AsmRegOperand> for AnyHasName {
|
||||
#[inline]
|
||||
fn from(node: AsmRegOperand) -> AnyHasName { AnyHasName { syntax: node.syntax } }
|
||||
}
|
||||
impl From<Const> for AnyHasName {
|
||||
#[inline]
|
||||
fn from(node: Const) -> AnyHasName { AnyHasName { syntax: node.syntax } }
|
||||
|
@ -6072,6 +6438,11 @@ impl std::fmt::Display for Adt {
|
|||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmOperand {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AssocItem {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
|
@ -6142,11 +6513,61 @@ impl std::fmt::Display for ArrayType {
|
|||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmClobberAbi {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmConst {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmDirSpec {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmExpr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmLabel {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmOperandExpr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmOption {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmOptions {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmRegOperand {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmRegSpec {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AsmSym {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AssocItemList {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
|
|
|
@ -19,6 +19,7 @@ stdx.workspace = true
|
|||
proc-macro2 = "1.0.47"
|
||||
quote = "1.0.20"
|
||||
ungrammar = "1.16.1"
|
||||
either.workspace = true
|
||||
itertools.workspace = true
|
||||
# Avoid adding more dependencies to this crate
|
||||
|
||||
|
|
|
@ -11,9 +11,11 @@ use std::{
|
|||
fs,
|
||||
};
|
||||
|
||||
use either::Either;
|
||||
use itertools::Itertools;
|
||||
use proc_macro2::{Punct, Spacing};
|
||||
use quote::{format_ident, quote};
|
||||
use stdx::panic_context;
|
||||
use ungrammar::{Grammar, Rule};
|
||||
|
||||
use crate::{
|
||||
|
@ -462,6 +464,7 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
|
|||
|
||||
let tokens = grammar.tokens.iter().map(|name| format_ident!("{}", name)).collect::<Vec<_>>();
|
||||
|
||||
// FIXME: This generates enum kinds?
|
||||
let nodes = grammar.nodes.iter().map(|name| format_ident!("{}", name)).collect::<Vec<_>>();
|
||||
|
||||
let ast = quote! {
|
||||
|
@ -711,6 +714,7 @@ fn lower(grammar: &Grammar) -> AstSrc {
|
|||
for &node in &nodes {
|
||||
let name = grammar[node].name.clone();
|
||||
let rule = &grammar[node].rule;
|
||||
let _g = panic_context::enter(name.clone());
|
||||
match lower_enum(grammar, rule) {
|
||||
Some(variants) => {
|
||||
let enum_src = AstEnumSrc { doc: Vec::new(), name, traits: Vec::new(), variants };
|
||||
|
@ -838,11 +842,16 @@ fn lower_separated_list(
|
|||
Rule::Seq(it) => it,
|
||||
_ => return false,
|
||||
};
|
||||
let (node, repeat, trailing_sep) = match rule.as_slice() {
|
||||
|
||||
let (nt, repeat, trailing_sep) = match rule.as_slice() {
|
||||
[Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => {
|
||||
(node, repeat, Some(trailing_sep))
|
||||
(Either::Left(node), repeat, Some(trailing_sep))
|
||||
}
|
||||
[Rule::Node(node), Rule::Rep(repeat)] => (node, repeat, None),
|
||||
[Rule::Node(node), Rule::Rep(repeat)] => (Either::Left(node), repeat, None),
|
||||
[Rule::Token(token), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => {
|
||||
(Either::Right(token), repeat, Some(trailing_sep))
|
||||
}
|
||||
[Rule::Token(token), Rule::Rep(repeat)] => (Either::Right(token), repeat, None),
|
||||
_ => return false,
|
||||
};
|
||||
let repeat = match &**repeat {
|
||||
|
@ -851,15 +860,28 @@ fn lower_separated_list(
|
|||
};
|
||||
if !matches!(
|
||||
repeat.as_slice(),
|
||||
[comma, Rule::Node(n)]
|
||||
if trailing_sep.map_or(true, |it| comma == &**it) && n == node
|
||||
[comma, nt_]
|
||||
if trailing_sep.map_or(true, |it| comma == &**it) && match (nt, nt_) {
|
||||
(Either::Left(node), Rule::Node(nt_)) => node == nt_,
|
||||
(Either::Right(token), Rule::Token(nt_)) => token == nt_,
|
||||
_ => false,
|
||||
}
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
match nt {
|
||||
Either::Right(token) => {
|
||||
let name = clean_token_name(&grammar[*token].name);
|
||||
let field = Field::Token(name);
|
||||
acc.push(field);
|
||||
}
|
||||
Either::Left(node) => {
|
||||
let ty = grammar[*node].name.clone();
|
||||
let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));
|
||||
let field = Field::Node { name, ty, cardinality: Cardinality::Many };
|
||||
acc.push(field);
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
@ -113,7 +113,31 @@ const RESERVED: &[&str] = &[
|
|||
const CONTEXTUAL_KEYWORDS: &[&str] =
|
||||
&["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet"];
|
||||
// keywords we use for special macro expansions
|
||||
const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &["builtin", "offset_of", "format_args", "asm"];
|
||||
const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
|
||||
"asm",
|
||||
"att_syntax",
|
||||
"builtin",
|
||||
"clobber_abi",
|
||||
"format_args",
|
||||
// "in",
|
||||
"inlateout",
|
||||
"inout",
|
||||
"label",
|
||||
"lateout",
|
||||
"may_unwind",
|
||||
"nomem",
|
||||
"noreturn",
|
||||
"nostack",
|
||||
"offset_of",
|
||||
"options",
|
||||
"out",
|
||||
"preserves_flags",
|
||||
"pure",
|
||||
// "raw",
|
||||
"readonly",
|
||||
"sym",
|
||||
];
|
||||
|
||||
// keywords that are keywords depending on the edition
|
||||
const EDITION_DEPENDENT_KEYWORDS: &[(&str, Edition)] = &[
|
||||
("try", Edition::Edition2018),
|
||||
|
|
Loading…
Reference in a new issue