diff --git a/parser.c b/parser.c index ddca1de313..1950f19431 100644 --- a/parser.c +++ b/parser.c @@ -39,6 +39,7 @@ static void parsePtrPayload(Parser*); static void parsePayload(Parser*); static AstNodeIndex parseSwitchExpr(Parser*); static AstNodeIndex parseForExpr(Parser*); +static AstNodeIndex parseAsmExpr(Parser*); typedef struct { enum { FIELD_STATE_NONE, FIELD_STATE_SEEN, FIELD_STATE_END } tag; @@ -1910,6 +1911,130 @@ static AstNodeIndex expectExpr(Parser* p) { return node; } +static AstNodeIndex parseAsmOutputItem(Parser* p) { + if (p->token_tags[p->tok_i] == TOKEN_L_BRACKET) { + p->tok_i++; // [ + const AstTokenIndex ident = expectToken(p, TOKEN_IDENTIFIER); + expectToken(p, TOKEN_R_BRACKET); + expectToken(p, TOKEN_STRING_LITERAL); + expectToken(p, TOKEN_L_PAREN); + AstNodeIndex operand = 0; + if (p->token_tags[p->tok_i] == TOKEN_ARROW) { + p->tok_i++; + operand = parseTypeExpr(p); + } + const AstTokenIndex rparen = expectToken(p, TOKEN_R_PAREN); + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_ASM_OUTPUT, + .main_token = ident, + .data = { .lhs = operand, .rhs = rparen }, + }); + } + return null_node; +} + +static AstNodeIndex parseAsmInputItem(Parser* p) { + if (p->token_tags[p->tok_i] == TOKEN_L_BRACKET) { + p->tok_i++; // [ + const AstTokenIndex ident = expectToken(p, TOKEN_IDENTIFIER); + expectToken(p, TOKEN_R_BRACKET); + expectToken(p, TOKEN_STRING_LITERAL); + expectToken(p, TOKEN_L_PAREN); + const AstNodeIndex operand = expectExpr(p); + const AstTokenIndex rparen = expectToken(p, TOKEN_R_PAREN); + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_ASM_INPUT, + .main_token = ident, + .data = { .lhs = operand, .rhs = rparen }, + }); + } + return null_node; +} + +static AstNodeIndex parseAsmExpr(Parser* p) { + const AstTokenIndex asm_token = nextToken(p); + assert(p->token_tags[asm_token] == TOKEN_KEYWORD_ASM); + eatToken(p, TOKEN_KEYWORD_VOLATILE); + expectToken(p, TOKEN_L_PAREN); + const AstNodeIndex template = expectExpr(p); + + // Simple asm: asm("...") + if (eatToken(p, TOKEN_R_PAREN) != null_token) { + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_ASM_SIMPLE, + .main_token = asm_token, + .data = { .lhs = template, .rhs = p->tok_i - 1 }, + }); + } + + // Complex asm with outputs, inputs, clobbers + expectToken(p, TOKEN_COLON); + + CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch))) + = initCleanupScratch(p); + + // Parse outputs + while (true) { + const AstNodeIndex output = parseAsmOutputItem(p); + if (output == 0) + break; + SLICE_APPEND(AstNodeIndex, &p->scratch, output); + if (eatToken(p, TOKEN_COMMA) == null_token) + break; + } + + // Parse inputs (after second colon) + if (eatToken(p, TOKEN_COLON) != null_token) { + while (true) { + const AstNodeIndex input = parseAsmInputItem(p); + if (input == 0) + break; + SLICE_APPEND(AstNodeIndex, &p->scratch, input); + if (eatToken(p, TOKEN_COMMA) == null_token) + break; + } + } + + // Parse clobbers (after third colon) + // Legacy format: "str1", "str2", ... + // New format: .{ .clobber = true } + AstNodeIndex clobbers = 0; + if (eatToken(p, TOKEN_COLON) != null_token) { + if (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL) { + // Legacy clobber format — skip all string literals and commas + while (p->token_tags[p->tok_i] == TOKEN_STRING_LITERAL) { + p->tok_i++; + if (eatToken(p, TOKEN_COMMA) == null_token) + break; + } + } else 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), + }, + }); +} + static AstNodeIndex parseSwitchExpr(Parser* p) { const AstTokenIndex switch_token = eatToken(p, TOKEN_KEYWORD_SWITCH); if (switch_token == null_token) @@ -2072,9 +2197,7 @@ static AstNodeIndex parsePrimaryExpr(Parser* p) { const char* tok = tokenizerGetTagString(p->token_tags[p->tok_i]); switch (p->token_tags[p->tok_i]) { case TOKEN_KEYWORD_ASM: - fprintf(stderr, "parsePrimaryExpr does not implement %s\n", tok); - exit(1); - break; + return parseAsmExpr(p); case TOKEN_KEYWORD_IF: return parseIfExpr(p); case TOKEN_KEYWORD_BREAK: