parser: implement asm_legacy, port inline asm tests
Add AST_NODE_ASM_LEGACY for legacy string clobber format.
When asm clobbers use string literals ("clobber1", "clobber2"),
produce asm_legacy node instead of asm node.
Port tests:
- "preserves clobbers in inline asm with stray comma"
- "remove trailing comma at the end of assembly clobber"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
3
ast.h
3
ast.h
@@ -484,6 +484,9 @@ typedef enum {
|
|||||||
AST_NODE_BLOCK_SEMICOLON,
|
AST_NODE_BLOCK_SEMICOLON,
|
||||||
/// `asm(lhs)`. rhs is the token index of the rparen.
|
/// `asm(lhs)`. rhs is the token index of the rparen.
|
||||||
AST_NODE_ASM_SIMPLE,
|
AST_NODE_ASM_SIMPLE,
|
||||||
|
/// Legacy asm with string clobbers. `asm(lhs, a)`.
|
||||||
|
/// `AsmLegacy[rhs]`.
|
||||||
|
AST_NODE_ASM_LEGACY,
|
||||||
/// `asm(lhs, a)`. `Asm[rhs]`.
|
/// `asm(lhs, a)`. `Asm[rhs]`.
|
||||||
AST_NODE_ASM,
|
AST_NODE_ASM,
|
||||||
/// `[a] "b" (c)`. lhs is 0, rhs is token index of the rparen.
|
/// `[a] "b" (c)`. lhs is 0, rhs is token index of the rparen.
|
||||||
|
|||||||
52
parser.c
52
parser.c
@@ -2127,28 +2127,60 @@ static AstNodeIndex parseAsmExpr(Parser* p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse clobbers (after third colon)
|
// Parse clobbers (after third colon)
|
||||||
// Legacy format: "str1", "str2", ...
|
|
||||||
// New format: .{ .clobber = true }
|
|
||||||
AstNodeIndex clobbers = 0;
|
|
||||||
if (eatToken(p, TOKEN_COLON) != null_token) {
|
if (eatToken(p, TOKEN_COLON) != null_token) {
|
||||||
if (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL) {
|
if (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL) {
|
||||||
// Legacy clobber format — skip all string literals and commas
|
// Legacy clobber format: "str1", "str2", ...
|
||||||
|
// Produces asm_legacy node
|
||||||
while (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL) {
|
while (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL) {
|
||||||
p->tok_i++;
|
p->tok_i++;
|
||||||
if (eatToken(p, TOKEN_COMMA) == null_token)
|
if (eatToken(p, TOKEN_COMMA) == null_token)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (p->token_tags[p->tok_i] != TOKEN_R_PAREN) {
|
const AstTokenIndex rparen = expectToken(p, TOKEN_R_PAREN);
|
||||||
clobbers = expectExpr(p);
|
const uint32_t items_len = p->scratch.len - scratch_top.old_len;
|
||||||
|
const AstSubRange items_span = listToSpan(
|
||||||
|
p, &p->scratch.arr[scratch_top.old_len], items_len);
|
||||||
|
return addNode(&p->nodes,
|
||||||
|
(AstNodeItem) {
|
||||||
|
.tag = AST_NODE_ASM_LEGACY,
|
||||||
|
.main_token = asm_token,
|
||||||
|
.data = {
|
||||||
|
.lhs = template,
|
||||||
|
.rhs = addExtra(p,
|
||||||
|
(AstNodeIndex[]) { items_span.start,
|
||||||
|
items_span.end, rparen },
|
||||||
|
3),
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
// New clobber format: expression (e.g. .{ .clobber = true })
|
||||||
|
AstNodeIndex clobbers = 0;
|
||||||
|
if (p->token_tags[p->tok_i] != TOKEN_R_PAREN)
|
||||||
|
clobbers = expectExpr(p);
|
||||||
|
|
||||||
|
const AstTokenIndex rparen = expectToken(p, TOKEN_R_PAREN);
|
||||||
|
const uint32_t items_len = p->scratch.len - scratch_top.old_len;
|
||||||
|
const AstSubRange items_span
|
||||||
|
= listToSpan(p, &p->scratch.arr[scratch_top.old_len], items_len);
|
||||||
|
return addNode(&p->nodes,
|
||||||
|
(AstNodeItem) {
|
||||||
|
.tag = AST_NODE_ASM,
|
||||||
|
.main_token = asm_token,
|
||||||
|
.data = {
|
||||||
|
.lhs = template,
|
||||||
|
.rhs = addExtra(p,
|
||||||
|
(AstNodeIndex[]) { items_span.start,
|
||||||
|
items_span.end, OPT(clobbers), rparen },
|
||||||
|
4),
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No clobbers
|
||||||
const AstTokenIndex rparen = expectToken(p, TOKEN_R_PAREN);
|
const AstTokenIndex rparen = expectToken(p, TOKEN_R_PAREN);
|
||||||
|
|
||||||
const uint32_t items_len = p->scratch.len - scratch_top.old_len;
|
const uint32_t items_len = p->scratch.len - scratch_top.old_len;
|
||||||
const AstSubRange items_span
|
const AstSubRange items_span
|
||||||
= listToSpan(p, &p->scratch.arr[scratch_top.old_len], items_len);
|
= listToSpan(p, &p->scratch.arr[scratch_top.old_len], items_len);
|
||||||
|
|
||||||
return addNode(&p->nodes,
|
return addNode(&p->nodes,
|
||||||
(AstNodeItem) {
|
(AstNodeItem) {
|
||||||
.tag = AST_NODE_ASM,
|
.tag = AST_NODE_ASM,
|
||||||
@@ -2156,8 +2188,8 @@ static AstNodeIndex parseAsmExpr(Parser* p) {
|
|||||||
.data = {
|
.data = {
|
||||||
.lhs = template,
|
.lhs = template,
|
||||||
.rhs = addExtra(p,
|
.rhs = addExtra(p,
|
||||||
(AstNodeIndex[]) { items_span.start,
|
(AstNodeIndex[]) { items_span.start, items_span.end,
|
||||||
items_span.end, OPT(clobbers), rparen },
|
OPT((AstNodeIndex)0), rparen },
|
||||||
4),
|
4),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ fn zigNode(token: c_uint) Ast.Node.Tag {
|
|||||||
c.AST_NODE_BLOCK => .block,
|
c.AST_NODE_BLOCK => .block,
|
||||||
c.AST_NODE_BLOCK_SEMICOLON => .block_semicolon,
|
c.AST_NODE_BLOCK_SEMICOLON => .block_semicolon,
|
||||||
c.AST_NODE_ASM_SIMPLE => .asm_simple,
|
c.AST_NODE_ASM_SIMPLE => .asm_simple,
|
||||||
|
c.AST_NODE_ASM_LEGACY => .asm_legacy,
|
||||||
c.AST_NODE_ASM => .@"asm",
|
c.AST_NODE_ASM => .@"asm",
|
||||||
c.AST_NODE_ASM_OUTPUT => .asm_output,
|
c.AST_NODE_ASM_OUTPUT => .asm_output,
|
||||||
c.AST_NODE_ASM_INPUT => .asm_input,
|
c.AST_NODE_ASM_INPUT => .asm_input,
|
||||||
@@ -569,6 +570,60 @@ test "zig fmt: tuple struct" {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "zig fmt: preserves clobbers in inline asm with stray comma" {
|
||||||
|
try testTransform(
|
||||||
|
\\fn foo() void {
|
||||||
|
\\ asm volatile (""
|
||||||
|
\\ : [_] "" (-> type),
|
||||||
|
\\ :
|
||||||
|
\\ : "clobber"
|
||||||
|
\\ );
|
||||||
|
\\ asm volatile (""
|
||||||
|
\\ :
|
||||||
|
\\ : [_] "" (type),
|
||||||
|
\\ : "clobber"
|
||||||
|
\\ );
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
,
|
||||||
|
\\fn foo() void {
|
||||||
|
\\ asm volatile (""
|
||||||
|
\\ : [_] "" (-> type),
|
||||||
|
\\ :
|
||||||
|
\\ : .{ .clobber = true }
|
||||||
|
\\ );
|
||||||
|
\\ asm volatile (""
|
||||||
|
\\ :
|
||||||
|
\\ : [_] "" (type),
|
||||||
|
\\ : .{ .clobber = true }
|
||||||
|
\\ );
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zig fmt: remove trailing comma at the end of assembly clobber" {
|
||||||
|
try testTransform(
|
||||||
|
\\fn foo() void {
|
||||||
|
\\ asm volatile (""
|
||||||
|
\\ : [_] "" (-> type),
|
||||||
|
\\ :
|
||||||
|
\\ : "clobber1", "clobber2",
|
||||||
|
\\ );
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
,
|
||||||
|
\\fn foo() void {
|
||||||
|
\\ asm volatile (""
|
||||||
|
\\ : [_] "" (-> type),
|
||||||
|
\\ :
|
||||||
|
\\ : .{ .clobber1 = true, .clobber2 = true }
|
||||||
|
\\ );
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
test "zig fmt: respect line breaks in struct field value declaration" {
|
test "zig fmt: respect line breaks in struct field value declaration" {
|
||||||
try testCanonical(
|
try testCanonical(
|
||||||
\\const Foo = struct {
|
\\const Foo = struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user