Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--.typos.toml1
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--crates/hir-def/src/body/lower.rs7
-rw-r--r--crates/hir-def/src/hir.rs5
-rw-r--r--crates/hir-ty/src/infer/closure.rs4
-rw-r--r--crates/hir-ty/src/infer/expr.rs2
-rw-r--r--crates/hir-ty/src/infer/mutability.rs7
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs171
-rw-r--r--crates/parser/src/parser.rs8
-rw-r--r--crates/parser/src/syntax_kind/generated.rs77
-rw-r--r--crates/parser/test_data/generated/runner.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/asm_expr.rast77
-rw-r--r--crates/parser/test_data/parser/inline/ok/asm_expr.rs10
-rw-r--r--crates/parser/test_data/parser/inline/ok/builtin_expr.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/builtin_expr.rs2
-rw-r--r--crates/syntax/rust.ungram27
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs425
-rw-r--r--xtask/Cargo.toml1
-rw-r--r--xtask/src/codegen/grammar.rs40
-rw-r--r--xtask/src/codegen/grammar/ast_src.rs26
21 files changed, 865 insertions, 31 deletions
diff --git a/.typos.toml b/.typos.toml
index e7e764ce03..febfb233bd 100644
--- a/.typos.toml
+++ b/.typos.toml
@@ -15,6 +15,7 @@ extend-ignore-re = [
'"flate2"',
"raison d'ĂȘtre",
"inout",
+ "INOUT",
"optin"
]
diff --git a/Cargo.lock b/Cargo.lock
index 6eaded6da1..85ef3a6ba9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2624,6 +2624,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"directories",
+ "either",
"flate2",
"itertools",
"proc-macro2",
diff --git a/Cargo.toml b/Cargo.toml
index e55628c8dc..9e972e1211 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 145afc1b61..f2eb43beb1 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -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()));
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 86fd092603..8f537672b5 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -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);
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 36327d1d49..67a3d2434d 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -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);
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index b1c793a1e3..6b725d690d 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -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()
}
};
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index 7fed5f0203..e1b460d072 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -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());
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index a678c1f3a7..57f1e6e9f0 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -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() {
// [];
diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs
index 7d3eb5de25..f6b3783d1c 100644
--- a/crates/parser/src/parser.rs
+++ b/crates/parser/src/parser.rs
@@ -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
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 00f212487a..ee3adac158 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -111,16 +111,32 @@ pub enum SyntaxKind {
YIELD_KW,
ASM_KW,
ASYNC_KW,
+ ATT_SYNTAX_KW,
AUTO_KW,
AWAIT_KW,
BUILTIN_KW,
+ CLOBBER_ABI_KW,
DEFAULT_KW,
DYN_KW,
FORMAT_ARGS_KW,
GEN_KW,
+ INLATEOUT_KW,
+ INOUT_KW,
+ LABEL_KW,
+ LATEOUT_KW,
MACRO_RULES_KW,
+ MAY_UNWIND_KW,
+ NOMEM_KW,
+ NORETURN_KW,
+ NOSTACK_KW,
OFFSET_OF_KW,
+ OPTIONS_KW,
+ OUT_KW,
+ PRESERVES_FLAGS_KW,
+ PURE_KW,
RAW_KW,
+ READONLY_KW,
+ SYM_KW,
TRY_KW,
UNION_KW,
YEET_KW,
@@ -146,7 +162,18 @@ pub enum SyntaxKind {
ARG_LIST,
ARRAY_EXPR,
ARRAY_TYPE,
+ ASM_CLOBBER_ABI,
+ ASM_CONST,
+ ASM_DIR_SPEC,
ASM_EXPR,
+ ASM_LABEL,
+ ASM_OPERAND,
+ ASM_OPERAND_EXPR,
+ ASM_OPTION,
+ ASM_OPTIONS,
+ ASM_REG_OPERAND,
+ ASM_REG_SPEC,
+ ASM_SYM,
ASSOC_ITEM,
ASSOC_ITEM_LIST,
ASSOC_TYPE_ARG,
@@ -364,14 +391,30 @@ impl SyntaxKind {
pub fn is_contextual_keyword(self, edition: Edition) -> bool {
match self {
ASM_KW => true,
+ ATT_SYNTAX_KW => true,
AUTO_KW => true,
BUILTIN_KW => true,
+ CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
FORMAT_ARGS_KW => true,
+ INLATEOUT_KW => true,
+ INOUT_KW => true,
+ LABEL_KW => true,
+ LATEOUT_KW => true,
MACRO_RULES_KW => true,
+ MAY_UNWIND_KW => true,
+ NOMEM_KW => true,
+ NORETURN_KW => true,
+ NOSTACK_KW => true,
OFFSET_OF_KW => true,
+ OPTIONS_KW => true,
+ OUT_KW => true,
+ PRESERVES_FLAGS_KW => true,
+ PURE_KW => true,
RAW_KW => true,
+ READONLY_KW => true,
+ SYM_KW => true,
UNION_KW => true,
YEET_KW => true,
_ => false,
@@ -435,14 +478,30 @@ impl SyntaxKind {
GEN_KW if Edition::Edition2024 <= edition => true,
TRY_KW if Edition::Edition2018 <= edition => true,
ASM_KW => true,
+ ATT_SYNTAX_KW => true,
AUTO_KW => true,
BUILTIN_KW => true,
+ CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
FORMAT_ARGS_KW => true,
+ INLATEOUT_KW => true,
+ INOUT_KW => true,
+ LABEL_KW => true,
+ LATEOUT_KW => true,
MACRO_RULES_KW => true,
+ MAY_UNWIND_KW => true,
+ NOMEM_KW => true,
+ NORETURN_KW => true,
+ NOSTACK_KW => true,
OFFSET_OF_KW => true,
+ OPTIONS_KW => true,
+ OUT_KW => true,
+ PRESERVES_FLAGS_KW => true,
+ PURE_KW => true,
RAW_KW => true,
+ READONLY_KW => true,
+ SYM_KW => true,
UNION_KW => true,
YEET_KW => true,
_ => false,
@@ -580,14 +639,30 @@ impl SyntaxKind {
pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option<SyntaxKind> {
let kw = match ident {
"asm" => ASM_KW,
+ "att_syntax" => ATT_SYNTAX_KW,
"auto" => AUTO_KW,
"builtin" => BUILTIN_KW,
+ "clobber_abi" => CLOBBER_ABI_KW,
"default" => DEFAULT_KW,
"dyn" if edition < Edition::Edition2018 => DYN_KW,
"format_args" => FORMAT_ARGS_KW,
+ "inlateout" => INLATEOUT_KW,
+ "inout" => INOUT_KW,
+ "label" => LABEL_KW,
+ "lateout" => LATEOUT_KW,
"macro_rules" => MACRO_RULES_KW,
+ "may_unwind" => MAY_UNWIND_KW,
+ "nomem" => NOMEM_KW,
+ "noreturn" => NORETURN_KW,
+ "nostack" => NOSTACK_KW,
"offset_of" => OFFSET_OF_KW,
+ "options" => OPTIONS_KW,
+ "out" => OUT_KW,
+ "preserves_flags" => PRESERVES_FLAGS_KW,
+ "pure" => PURE_KW,
"raw" => RAW_KW,
+ "readonly" => READONLY_KW,
+ "sym" => SYM_KW,
"union" => UNION_KW,
"yeet" => YEET_KW,
_ => return None,
@@ -630,4 +705,4 @@ impl SyntaxKind {
}
}
#[macro_export]
-macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 9ce5a2ae74..164d0f36f1 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -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");
}
diff --git a/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/crates/parser/test_data/parser/inline/ok/asm_expr.rast
new file mode 100644
index 0000000000..f4d53fa9ae
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/asm_expr.rast
@@ -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"
diff --git a/crates/parser/test_data/parser/inline/ok/asm_expr.rs b/crates/parser/test_data/parser/inline/ok/asm_expr.rs
new file mode 100644
index 0000000000..0906cd3e71
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/asm_expr.rs
@@ -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) _,
+ );
+}
diff --git a/crates/parser/test_data/parser/inline/ok/builtin_expr.rast b/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
index 361900b6d3..19a84ac540 100644
--- a/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
@@ -19,7 +19,7 @@ SOURCE_FILE
ASM_KW "asm"
L_PAREN "("
LITERAL
- INT_NUMBER "0"
+ STRING "\"\""
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n "
diff --git a/crates/parser/test_data/parser/inline/ok/builtin_expr.rs b/crates/parser/test_data/parser/inline/ok/builtin_expr.rs
index 14431b0210..920d0f794f 100644
--- a/crates/parser/test_data/parser/inline/ok/builtin_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/builtin_expr.rs
@@ -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);
}
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 43375ce6ae..4d780ba28f 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -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' '('
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index c9b39e9922..e5e1115e05 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -65,13 +65,62 @@ impl ArrayType {
}
#[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,
}
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,12 +128,134 @@ 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,
}
@@ -2052,6 +2223,14 @@ 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),
Fn(Fn),
@@ -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)
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 192de86947..4bc1821ee5 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -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
diff --git a/xtask/src/codegen/grammar.rs b/xtask/src/codegen/grammar.rs
index 39e06f9642..e7534582f2 100644
--- a/xtask/src/codegen/grammar.rs
+++ b/xtask/src/codegen/grammar.rs
@@ -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)] => (Either::Left(node), repeat, None),
+ [Rule::Token(token), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => {
+ (Either::Right(token), repeat, Some(trailing_sep))
}
- [Rule::Node(node), Rule::Rep(repeat)] => (node, repeat, None),
+ [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;
}
- 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);
+ 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
}
diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs
index 34151bd958..f1a96e0c6a 100644
--- a/xtask/src/codegen/grammar/ast_src.rs
+++ b/xtask/src/codegen/grammar/ast_src.rs
@@ -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),