Parse builtin#asm expressions

This commit is contained in:
Lukas Wirth 2024-09-01 13:43:05 +02:00
parent 50882fbfa2
commit 86658c66b4
21 changed files with 865 additions and 31 deletions

View file

@ -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() {
// [];

View file

@ -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

View file

@ -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");
}

View 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"

View 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) _,
);
}

View file

@ -19,7 +19,7 @@ SOURCE_FILE
ASM_KW "asm"
L_PAREN "("
LITERAL
INT_NUMBER "0"
STRING "\"\""
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n "

View file

@ -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);
}