diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 9b2d8f9d54..169c6cbb67 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -419,13 +419,16 @@ pub const Tree = struct { n = extra.start; }, + .AsmOutput, .AsmInput => { + assert(token_tags[main_tokens[n] - 1] == .LBracket); + return main_tokens[n] - 1; + }, + .WhileSimple => unreachable, // TODO .WhileCont => unreachable, // TODO .While => unreachable, // TODO .ForSimple => unreachable, // TODO .For => unreachable, // TODO - .AsmOutput => unreachable, // TODO - .AsmInput => unreachable, // TODO .ErrorValue => unreachable, // TODO }; } @@ -515,6 +518,9 @@ pub const Tree = struct { .GroupedExpression, .StringLiteral, .ErrorSetDecl, + .AsmSimple, + .AsmOutput, + .AsmInput, => return datas[n].rhs + end_offset, .AnyType, @@ -566,6 +572,10 @@ pub const Tree = struct { n = tree.extra_data[members.end - 1]; // last parameter } }, + .Asm => { + const extra = tree.extraData(datas[n].rhs, Node.Asm); + return extra.rparen + end_offset; + }, .ContainerDeclArgComma, .SwitchComma, => { @@ -765,8 +775,6 @@ pub const Tree = struct { .TaggedUnionEnumTagComma => unreachable, // TODO .If => unreachable, // TODO .Continue => unreachable, // TODO - .AsmSimple => unreachable, // TODO - .Asm => unreachable, // TODO .SwitchRange => unreachable, // TODO .ArrayType => unreachable, // TODO .ArrayTypeSentinel => unreachable, // TODO @@ -778,8 +786,6 @@ pub const Tree = struct { .FnProtoMulti => unreachable, // TODO .FnProtoOne => unreachable, // TODO .FnProto => unreachable, // TODO - .AsmOutput => unreachable, // TODO - .AsmInput => unreachable, // TODO .ErrorValue => unreachable, // TODO }; } @@ -790,7 +796,7 @@ pub const Tree = struct { return mem.indexOfScalar(u8, source, '\n') == null; } - pub fn globalVarDecl(tree: Tree, node: Node.Index) Full.VarDecl { + pub fn globalVarDecl(tree: Tree, node: Node.Index) full.VarDecl { assert(tree.nodes.items(.tag)[node] == .GlobalVarDecl); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.GlobalVarDecl); @@ -803,7 +809,7 @@ pub const Tree = struct { }); } - pub fn localVarDecl(tree: Tree, node: Node.Index) Full.VarDecl { + pub fn localVarDecl(tree: Tree, node: Node.Index) full.VarDecl { assert(tree.nodes.items(.tag)[node] == .LocalVarDecl); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.LocalVarDecl); @@ -816,7 +822,7 @@ pub const Tree = struct { }); } - pub fn simpleVarDecl(tree: Tree, node: Node.Index) Full.VarDecl { + pub fn simpleVarDecl(tree: Tree, node: Node.Index) full.VarDecl { assert(tree.nodes.items(.tag)[node] == .SimpleVarDecl); const data = tree.nodes.items(.data)[node]; return tree.fullVarDecl(.{ @@ -828,7 +834,7 @@ pub const Tree = struct { }); } - pub fn alignedVarDecl(tree: Tree, node: Node.Index) Full.VarDecl { + pub fn alignedVarDecl(tree: Tree, node: Node.Index) full.VarDecl { assert(tree.nodes.items(.tag)[node] == .AlignedVarDecl); const data = tree.nodes.items(.data)[node]; return tree.fullVarDecl(.{ @@ -840,7 +846,7 @@ pub const Tree = struct { }); } - pub fn ifSimple(tree: Tree, node: Node.Index) Full.If { + pub fn ifSimple(tree: Tree, node: Node.Index) full.If { assert(tree.nodes.items(.tag)[node] == .IfSimple); const data = tree.nodes.items(.data)[node]; return tree.fullIf(.{ @@ -851,7 +857,7 @@ pub const Tree = struct { }); } - pub fn ifFull(tree: Tree, node: Node.Index) Full.If { + pub fn ifFull(tree: Tree, node: Node.Index) full.If { assert(tree.nodes.items(.tag)[node] == .If); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.If); @@ -863,7 +869,7 @@ pub const Tree = struct { }); } - pub fn containerField(tree: Tree, node: Node.Index) Full.ContainerField { + pub fn containerField(tree: Tree, node: Node.Index) full.ContainerField { assert(tree.nodes.items(.tag)[node] == .ContainerField); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.ContainerField); @@ -875,7 +881,7 @@ pub const Tree = struct { }); } - pub fn containerFieldInit(tree: Tree, node: Node.Index) Full.ContainerField { + pub fn containerFieldInit(tree: Tree, node: Node.Index) full.ContainerField { assert(tree.nodes.items(.tag)[node] == .ContainerFieldInit); const data = tree.nodes.items(.data)[node]; return tree.fullContainerField(.{ @@ -886,7 +892,7 @@ pub const Tree = struct { }); } - pub fn containerFieldAlign(tree: Tree, node: Node.Index) Full.ContainerField { + pub fn containerFieldAlign(tree: Tree, node: Node.Index) full.ContainerField { assert(tree.nodes.items(.tag)[node] == .ContainerFieldAlign); const data = tree.nodes.items(.data)[node]; return tree.fullContainerField(.{ @@ -897,7 +903,7 @@ pub const Tree = struct { }); } - pub fn fnProtoSimple(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) Full.FnProto { + pub fn fnProtoSimple(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnProto { assert(tree.nodes.items(.tag)[node] == .FnProtoSimple); const data = tree.nodes.items(.data)[node]; buffer[0] = data.lhs; @@ -912,7 +918,7 @@ pub const Tree = struct { }); } - pub fn fnProtoMulti(tree: Tree, node: Node.Index) Full.FnProto { + pub fn fnProtoMulti(tree: Tree, node: Node.Index) full.FnProto { assert(tree.nodes.items(.tag)[node] == .FnProtoMulti); const data = tree.nodes.items(.data)[node]; const params_range = tree.extraData(data.lhs, Node.SubRange); @@ -927,7 +933,7 @@ pub const Tree = struct { }); } - pub fn fnProtoOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) Full.FnProto { + pub fn fnProtoOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.FnProto { assert(tree.nodes.items(.tag)[node] == .FnProtoOne); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.FnProtoOne); @@ -943,7 +949,7 @@ pub const Tree = struct { }); } - pub fn fnProto(tree: Tree, node: Node.Index) Full.FnProto { + pub fn fnProto(tree: Tree, node: Node.Index) full.FnProto { assert(tree.nodes.items(.tag)[node] == .FnProto); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.FnProto); @@ -958,7 +964,7 @@ pub const Tree = struct { }); } - pub fn structInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) Full.StructInit { + pub fn structInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.StructInit { assert(tree.nodes.items(.tag)[node] == .StructInitOne); const data = tree.nodes.items(.data)[node]; buffer[0] = data.rhs; @@ -970,7 +976,7 @@ pub const Tree = struct { }); } - pub fn structInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) Full.StructInit { + pub fn structInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.StructInit { assert(tree.nodes.items(.tag)[node] == .StructInitDotTwo or tree.nodes.items(.tag)[node] == .StructInitDotTwoComma); const data = tree.nodes.items(.data)[node]; @@ -988,7 +994,7 @@ pub const Tree = struct { }); } - pub fn structInitDot(tree: Tree, node: Node.Index) Full.StructInit { + pub fn structInitDot(tree: Tree, node: Node.Index) full.StructInit { assert(tree.nodes.items(.tag)[node] == .StructInitDot); const data = tree.nodes.items(.data)[node]; return tree.fullStructInit(.{ @@ -998,7 +1004,7 @@ pub const Tree = struct { }); } - pub fn structInit(tree: Tree, node: Node.Index) Full.StructInit { + pub fn structInit(tree: Tree, node: Node.Index) full.StructInit { assert(tree.nodes.items(.tag)[node] == .StructInit); const data = tree.nodes.items(.data)[node]; const fields_range = tree.extraData(data.rhs, Node.SubRange); @@ -1009,7 +1015,7 @@ pub const Tree = struct { }); } - pub fn arrayInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) Full.ArrayInit { + pub fn arrayInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.ArrayInit { assert(tree.nodes.items(.tag)[node] == .ArrayInitOne); const data = tree.nodes.items(.data)[node]; buffer[0] = data.rhs; @@ -1023,7 +1029,7 @@ pub const Tree = struct { }; } - pub fn arrayInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) Full.ArrayInit { + pub fn arrayInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ArrayInit { assert(tree.nodes.items(.tag)[node] == .ArrayInitDotTwo or tree.nodes.items(.tag)[node] == .ArrayInitDotTwoComma); const data = tree.nodes.items(.data)[node]; @@ -1043,7 +1049,7 @@ pub const Tree = struct { }; } - pub fn arrayInitDot(tree: Tree, node: Node.Index) Full.ArrayInit { + pub fn arrayInitDot(tree: Tree, node: Node.Index) full.ArrayInit { assert(tree.nodes.items(.tag)[node] == .ArrayInitDot); const data = tree.nodes.items(.data)[node]; return .{ @@ -1055,7 +1061,7 @@ pub const Tree = struct { }; } - pub fn arrayInit(tree: Tree, node: Node.Index) Full.ArrayInit { + pub fn arrayInit(tree: Tree, node: Node.Index) full.ArrayInit { assert(tree.nodes.items(.tag)[node] == .ArrayInit); const data = tree.nodes.items(.data)[node]; const elem_range = tree.extraData(data.rhs, Node.SubRange); @@ -1068,7 +1074,7 @@ pub const Tree = struct { }; } - pub fn arrayType(tree: Tree, node: Node.Index) Full.ArrayType { + pub fn arrayType(tree: Tree, node: Node.Index) full.ArrayType { assert(tree.nodes.items(.tag)[node] == .ArrayType); const data = tree.nodes.items(.data)[node]; return .{ @@ -1081,7 +1087,7 @@ pub const Tree = struct { }; } - pub fn arrayTypeSentinel(tree: Tree, node: Node.Index) Full.ArrayType { + pub fn arrayTypeSentinel(tree: Tree, node: Node.Index) full.ArrayType { assert(tree.nodes.items(.tag)[node] == .ArrayTypeSentinel); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.ArrayTypeSentinel); @@ -1095,7 +1101,7 @@ pub const Tree = struct { }; } - pub fn ptrTypeAligned(tree: Tree, node: Node.Index) Full.PtrType { + pub fn ptrTypeAligned(tree: Tree, node: Node.Index) full.PtrType { assert(tree.nodes.items(.tag)[node] == .PtrTypeAligned); const data = tree.nodes.items(.data)[node]; return tree.fullPtrType(.{ @@ -1108,7 +1114,7 @@ pub const Tree = struct { }); } - pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) Full.PtrType { + pub fn ptrTypeSentinel(tree: Tree, node: Node.Index) full.PtrType { assert(tree.nodes.items(.tag)[node] == .PtrTypeSentinel); const data = tree.nodes.items(.data)[node]; return tree.fullPtrType(.{ @@ -1121,7 +1127,7 @@ pub const Tree = struct { }); } - pub fn ptrType(tree: Tree, node: Node.Index) Full.PtrType { + pub fn ptrType(tree: Tree, node: Node.Index) full.PtrType { assert(tree.nodes.items(.tag)[node] == .PtrType); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.PtrType); @@ -1135,7 +1141,7 @@ pub const Tree = struct { }); } - pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) Full.PtrType { + pub fn ptrTypeBitRange(tree: Tree, node: Node.Index) full.PtrType { assert(tree.nodes.items(.tag)[node] == .PtrTypeBitRange); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.PtrTypeBitRange); @@ -1149,7 +1155,7 @@ pub const Tree = struct { }); } - pub fn sliceOpen(tree: Tree, node: Node.Index) Full.Slice { + pub fn sliceOpen(tree: Tree, node: Node.Index) full.Slice { assert(tree.nodes.items(.tag)[node] == .SliceOpen); const data = tree.nodes.items(.data)[node]; return .{ @@ -1163,7 +1169,7 @@ pub const Tree = struct { }; } - pub fn slice(tree: Tree, node: Node.Index) Full.Slice { + pub fn slice(tree: Tree, node: Node.Index) full.Slice { assert(tree.nodes.items(.tag)[node] == .Slice); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.Slice); @@ -1178,7 +1184,7 @@ pub const Tree = struct { }; } - pub fn sliceSentinel(tree: Tree, node: Node.Index) Full.Slice { + pub fn sliceSentinel(tree: Tree, node: Node.Index) full.Slice { assert(tree.nodes.items(.tag)[node] == .SliceSentinel); const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.rhs, Node.SliceSentinel); @@ -1193,7 +1199,7 @@ pub const Tree = struct { }; } - pub fn containerDeclTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) Full.ContainerDecl { + pub fn containerDeclTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .ContainerDeclTwo or tree.nodes.items(.tag)[node] == .ContainerDeclTwoComma); const data = tree.nodes.items(.data)[node]; @@ -1212,7 +1218,7 @@ pub const Tree = struct { }); } - pub fn containerDecl(tree: Tree, node: Node.Index) Full.ContainerDecl { + pub fn containerDecl(tree: Tree, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .ContainerDecl or tree.nodes.items(.tag)[node] == .ContainerDeclComma); const data = tree.nodes.items(.data)[node]; @@ -1224,7 +1230,7 @@ pub const Tree = struct { }); } - pub fn containerDeclArg(tree: Tree, node: Node.Index) Full.ContainerDecl { + pub fn containerDeclArg(tree: Tree, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .ContainerDeclArg or tree.nodes.items(.tag)[node] == .ContainerDeclArgComma); const data = tree.nodes.items(.data)[node]; @@ -1237,7 +1243,7 @@ pub const Tree = struct { }); } - pub fn taggedUnionTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) Full.ContainerDecl { + pub fn taggedUnionTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .TaggedUnionTwo or tree.nodes.items(.tag)[node] == .TaggedUnionTwoComma); const data = tree.nodes.items(.data)[node]; @@ -1257,7 +1263,7 @@ pub const Tree = struct { }); } - pub fn taggedUnion(tree: Tree, node: Node.Index) Full.ContainerDecl { + pub fn taggedUnion(tree: Tree, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .TaggedUnion or tree.nodes.items(.tag)[node] == .TaggedUnionComma); const data = tree.nodes.items(.data)[node]; @@ -1270,7 +1276,7 @@ pub const Tree = struct { }); } - pub fn taggedUnionEnumTag(tree: Tree, node: Node.Index) Full.ContainerDecl { + pub fn taggedUnionEnumTag(tree: Tree, node: Node.Index) full.ContainerDecl { assert(tree.nodes.items(.tag)[node] == .TaggedUnionEnumTag or tree.nodes.items(.tag)[node] == .TaggedUnionEnumTagComma); const data = tree.nodes.items(.data)[node]; @@ -1284,7 +1290,7 @@ pub const Tree = struct { }); } - pub fn switchCaseOne(tree: Tree, node: Node.Index) Full.SwitchCase { + pub fn switchCaseOne(tree: Tree, node: Node.Index) full.SwitchCase { const data = &tree.nodes.items(.data)[node]; return tree.fullSwitchCase(.{ .values = if (data.lhs == 0) &.{} else @ptrCast([*]Node.Index, &data.lhs)[0..1], @@ -1293,7 +1299,7 @@ pub const Tree = struct { }); } - pub fn switchCase(tree: Tree, node: Node.Index) Full.SwitchCase { + pub fn switchCase(tree: Tree, node: Node.Index) full.SwitchCase { const data = tree.nodes.items(.data)[node]; const extra = tree.extraData(data.lhs, Node.SubRange); return tree.fullSwitchCase(.{ @@ -1303,9 +1309,30 @@ pub const Tree = struct { }); } - fn fullVarDecl(tree: Tree, info: Full.VarDecl.Ast) Full.VarDecl { + pub fn asmSimple(tree: Tree, node: Node.Index) full.Asm { + const data = tree.nodes.items(.data)[node]; + return tree.fullAsm(.{ + .asm_token = tree.nodes.items(.main_token)[node], + .template = data.lhs, + .items = &.{}, + .rparen = data.rhs, + }); + } + + pub fn asmFull(tree: Tree, node: Node.Index) full.Asm { + const data = tree.nodes.items(.data)[node]; + const extra = tree.extraData(data.rhs, Node.Asm); + return tree.fullAsm(.{ + .asm_token = tree.nodes.items(.main_token)[node], + .template = data.lhs, + .items = tree.extra_data[extra.items_start..extra.items_end], + .rparen = extra.rparen, + }); + } + + fn fullVarDecl(tree: Tree, info: full.VarDecl.Ast) full.VarDecl { const token_tags = tree.tokens.items(.tag); - var result: Full.VarDecl = .{ + var result: full.VarDecl = .{ .ast = info, .visib_token = null, .extern_export_token = null, @@ -1328,9 +1355,9 @@ pub const Tree = struct { return result; } - fn fullIf(tree: Tree, info: Full.If.Ast) Full.If { + fn fullIf(tree: Tree, info: full.If.Ast) full.If { const token_tags = tree.tokens.items(.tag); - var result: Full.If = .{ + var result: full.If = .{ .ast = info, .payload_token = null, .error_token = null, @@ -1353,9 +1380,9 @@ pub const Tree = struct { return result; } - fn fullContainerField(tree: Tree, info: Full.ContainerField.Ast) Full.ContainerField { + fn fullContainerField(tree: Tree, info: full.ContainerField.Ast) full.ContainerField { const token_tags = tree.tokens.items(.tag); - var result: Full.ContainerField = .{ + var result: full.ContainerField = .{ .ast = info, .comptime_token = null, }; @@ -1367,27 +1394,27 @@ pub const Tree = struct { return result; } - fn fullFnProto(tree: Tree, info: Full.FnProto.Ast) Full.FnProto { + fn fullFnProto(tree: Tree, info: full.FnProto.Ast) full.FnProto { const token_tags = tree.tokens.items(.tag); - var result: Full.FnProto = .{ + var result: full.FnProto = .{ .ast = info, }; return result; } - fn fullStructInit(tree: Tree, info: Full.StructInit.Ast) Full.StructInit { + fn fullStructInit(tree: Tree, info: full.StructInit.Ast) full.StructInit { const token_tags = tree.tokens.items(.tag); - var result: Full.StructInit = .{ + var result: full.StructInit = .{ .ast = info, }; return result; } - fn fullPtrType(tree: Tree, info: Full.PtrType.Ast) Full.PtrType { + fn fullPtrType(tree: Tree, info: full.PtrType.Ast) full.PtrType { const token_tags = tree.tokens.items(.tag); // TODO: looks like stage1 isn't quite smart enough to handle enum // literals in some places here - const Kind = Full.PtrType.Kind; + const Kind = full.PtrType.Kind; const kind: Kind = switch (token_tags[info.main_token]) { .Asterisk => switch (token_tags[info.main_token + 1]) { .RBracket => .many, @@ -1402,7 +1429,7 @@ pub const Tree = struct { }, else => unreachable, }; - var result: Full.PtrType = .{ + var result: full.PtrType = .{ .kind = kind, .allowzero_token = null, .const_token = null, @@ -1441,9 +1468,9 @@ pub const Tree = struct { return result; } - fn fullContainerDecl(tree: Tree, info: Full.ContainerDecl.Ast) Full.ContainerDecl { + fn fullContainerDecl(tree: Tree, info: full.ContainerDecl.Ast) full.ContainerDecl { const token_tags = tree.tokens.items(.tag); - var result: Full.ContainerDecl = .{ + var result: full.ContainerDecl = .{ .ast = info, .layout_token = null, }; @@ -1454,9 +1481,9 @@ pub const Tree = struct { return result; } - fn fullSwitchCase(tree: Tree, info: Full.SwitchCase.Ast) Full.SwitchCase { + fn fullSwitchCase(tree: Tree, info: full.SwitchCase.Ast) full.SwitchCase { const token_tags = tree.tokens.items(.tag); - var result: Full.SwitchCase = .{ + var result: full.SwitchCase = .{ .ast = info, .payload_token = null, }; @@ -1465,10 +1492,67 @@ pub const Tree = struct { } return result; } + + fn fullAsm(tree: Tree, info: full.Asm.Ast) full.Asm { + const token_tags = tree.tokens.items(.tag); + const node_tags = tree.nodes.items(.tag); + var result: full.Asm = .{ + .ast = info, + .volatile_token = null, + .inputs = &.{}, + .outputs = &.{}, + .first_clobber = null, + }; + if (token_tags[info.asm_token + 1] == .Keyword_volatile) { + result.volatile_token = info.asm_token + 1; + } + const outputs_end: usize = for (info.items) |item, i| { + switch (node_tags[item]) { + .AsmOutput => continue, + else => break i, + } + } else info.items.len; + + result.outputs = info.items[0..outputs_end]; + result.inputs = info.items[outputs_end..]; + + if (info.items.len == 0) { + // asm ("foo" ::: "a", "b"); + const template_token = tree.lastToken(info.template); + if (token_tags[template_token + 1] == .Colon and + token_tags[template_token + 2] == .Colon and + token_tags[template_token + 3] == .Colon and + token_tags[template_token + 4] == .StringLiteral) + { + result.first_clobber = template_token + 4; + } + } else if (result.inputs.len != 0) { + // asm ("foo" :: [_] "" (y) : "a", "b"); + const last_input = result.inputs[result.inputs.len - 1]; + const rparen = tree.lastToken(last_input); + if (token_tags[rparen + 1] == .Colon and + token_tags[rparen + 2] == .StringLiteral) + { + result.first_clobber = rparen + 2; + } + } else { + // asm ("foo" : [_] "" (x) :: "a", "b"); + const last_output = result.outputs[result.outputs.len - 1]; + const rparen = tree.lastToken(last_output); + if (token_tags[rparen + 1] == .Colon and + token_tags[rparen + 2] == .Colon and + token_tags[rparen + 3] == .StringLiteral) + { + result.first_clobber = rparen + 3; + } + } + + return result; + } }; /// Fully assembled AST node information. -pub const Full = struct { +pub const full = struct { pub const VarDecl = struct { visib_token: ?TokenIndex, extern_export_token: ?TokenIndex, @@ -1624,6 +1708,21 @@ pub const Full = struct { target_expr: Node.Index, }; }; + + pub const Asm = struct { + ast: Ast, + volatile_token: ?TokenIndex, + first_clobber: ?TokenIndex, + outputs: []const Node.Index, + inputs: []const Node.Index, + + pub const Ast = struct { + asm_token: TokenIndex, + template: Node.Index, + items: []const Node.Index, + rparen: TokenIndex, + }; + }; }; pub const Error = union(enum) { @@ -2234,15 +2333,15 @@ pub const Node = struct { Block, /// Same as BlockTwo but there is known to be a semicolon before the rbrace. BlockSemicolon, - /// `asm(lhs)`. rhs unused. + /// `asm(lhs)`. rhs is the token index of the rparen. AsmSimple, - /// `asm(lhs, a)`. `sub_range_list[rhs]`. + /// `asm(lhs, a)`. `Asm[rhs]`. Asm, - /// `[a] "b" (c)`. lhs is string literal token index, rhs is 0. - /// `[a] "b" (-> rhs)`. lhs is the string literal token index, rhs is type expr. + /// `[a] "b" (c)`. lhs is 0, rhs is token index of the rparen. + /// `[a] "b" (-> lhs)`. rhs is token index of the rparen. /// main_token is `a`. AsmOutput, - /// `[a] "b" (rhs)`. lhs is string literal token index. + /// `[a] "b" (lhs)`. rhs is token index of the rparen. /// main_token is `a`. AsmInput, /// `error.a`. lhs is token index of `.`. rhs is token index of `a`. @@ -2355,4 +2454,11 @@ pub const Node = struct { /// Populated if callconv(A) is present. callconv_expr: Index, }; + + pub const Asm = struct { + items_start: Index, + items_end: Index, + /// Needed to make lastToken() work. + rparen: TokenIndex, + }; }; diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index aa70634c47..3ca5bb0049 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -472,7 +472,7 @@ const Parser = struct { /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block fn expectTestDecl(p: *Parser) !Node.Index { - const test_token = try p.expectToken(.Keyword_test); + const test_token = p.assertToken(.Keyword_test); const name_token = p.eatToken(.StringLiteral); const block_node = try p.parseBlock(); if (block_node == 0) return p.fail(.{ .ExpectedLBrace = .{ .token = p.tok_i } }); @@ -739,7 +739,7 @@ const Parser = struct { /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? fn expectContainerField(p: *Parser) !Node.Index { const comptime_token = p.eatToken(.Keyword_comptime); - const name_token = try p.expectToken(.Identifier); + const name_token = p.assertToken(.Identifier); var align_expr: Node.Index = 0; var type_expr: Node.Index = 0; @@ -1846,7 +1846,7 @@ const Parser = struct { /// / CurlySuffixExpr fn parsePrimaryExpr(p: *Parser) !Node.Index { switch (p.token_tags[p.tok_i]) { - .Keyword_asm => return p.parseAsmExpr(), + .Keyword_asm => return p.expectAsmExpr(), .Keyword_if => return p.parseIfExpr(), .Keyword_break => { p.tok_i += 1; @@ -2910,19 +2910,19 @@ const Parser = struct { /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL? /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? - fn parseAsmExpr(p: *Parser) !Node.Index { + fn expectAsmExpr(p: *Parser) !Node.Index { const asm_token = p.assertToken(.Keyword_asm); _ = p.eatToken(.Keyword_volatile); _ = try p.expectToken(.LParen); const template = try p.expectExpr(); - if (p.eatToken(.RParen)) |_| { + if (p.eatToken(.RParen)) |rparen| { return p.addNode(.{ .tag = .AsmSimple, .main_token = asm_token, .data = .{ .lhs = template, - .rhs = undefined, + .rhs = rparen, }, }); } @@ -2981,16 +2981,17 @@ const Parser = struct { } } } - _ = try p.expectToken(.RParen); + const rparen = try p.expectToken(.RParen); const span = try p.listToSpan(list.items); return p.addNode(.{ .tag = .Asm, .main_token = asm_token, .data = .{ .lhs = template, - .rhs = try p.addExtra(Node.SubRange{ - .start = span.start, - .end = span.end, + .rhs = try p.addExtra(Node.Asm{ + .items_start = span.start, + .items_end = span.end, + .rparen = rparen, }), }, }); @@ -3001,16 +3002,23 @@ const Parser = struct { _ = p.eatToken(.LBracket) orelse return null_node; const identifier = try p.expectToken(.Identifier); _ = try p.expectToken(.RBracket); - const constraint = try p.expectToken(.StringLiteral); + _ = try p.expectToken(.StringLiteral); _ = try p.expectToken(.LParen); - const rhs: Node.Index = if (p.eatToken(.Arrow)) |_| try p.expectTypeExpr() else null_node; - _ = try p.expectToken(.RParen); + const type_expr: Node.Index = blk: { + if (p.eatToken(.Arrow)) |_| { + break :blk try p.expectTypeExpr(); + } else { + _ = try p.expectToken(.Identifier); + break :blk null_node; + } + }; + const rparen = try p.expectToken(.RParen); return p.addNode(.{ .tag = .AsmOutput, .main_token = identifier, .data = .{ - .lhs = constraint, - .rhs = rhs, + .lhs = type_expr, + .rhs = rparen, }, }); } @@ -3020,16 +3028,16 @@ const Parser = struct { _ = p.eatToken(.LBracket) orelse return null_node; const identifier = try p.expectToken(.Identifier); _ = try p.expectToken(.RBracket); - const constraint = try p.expectToken(.StringLiteral); + _ = try p.expectToken(.StringLiteral); _ = try p.expectToken(.LParen); const expr = try p.expectExpr(); - _ = try p.expectToken(.RParen); + const rparen = try p.expectToken(.RParen); return p.addNode(.{ .tag = .AsmInput, .main_token = identifier, .data = .{ - .lhs = constraint, - .rhs = expr, + .lhs = expr, + .rhs = rparen, }, }); } diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 983190b152..5ae8f37b87 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -313,30 +313,30 @@ test "zig fmt: builtin call with trailing comma" { ); } -//test "zig fmt: asm expression with comptime content" { -// try testCanonical( -// \\comptime { -// \\ asm ("foo" ++ "bar"); -// \\} -// \\pub fn main() void { -// \\ asm volatile ("foo" ++ "bar"); -// \\ asm volatile ("foo" ++ "bar" -// \\ : [_] "" (x) -// \\ ); -// \\ asm volatile ("foo" ++ "bar" -// \\ : [_] "" (x) -// \\ : [_] "" (y) -// \\ ); -// \\ asm volatile ("foo" ++ "bar" -// \\ : [_] "" (x) -// \\ : [_] "" (y) -// \\ : "h", "e", "l", "l", "o" -// \\ ); -// \\} -// \\ -// ); -//} -// +test "zig fmt: asm expression with comptime content" { + try testCanonical( + \\comptime { + \\ asm ("foo" ++ "bar"); + \\} + \\pub fn main() void { + \\ asm volatile ("foo" ++ "bar"); + \\ asm volatile ("foo" ++ "bar" + \\ : [_] "" (x) + \\ ); + \\ asm volatile ("foo" ++ "bar" + \\ : [_] "" (x) + \\ : [_] "" (y) + \\ ); + \\ asm volatile ("foo" ++ "bar" + \\ : [_] "" (x) + \\ : [_] "" (y) + \\ : "h", "e", "l", "l", "o" + \\ ); + \\} + \\ + ); +} + //test "zig fmt: anytype struct field" { // try testCanonical( // \\pub const Pointer = struct { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index bd9b73e55d..212e4c85d1 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -816,118 +816,8 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac .IfSimple => return renderIf(ais, tree, tree.ifSimple(node), space), .If => return renderIf(ais, tree, tree.ifFull(node), space), - .Asm => unreachable, // TODO - .AsmSimple => unreachable, // TODO - .AsmOutput => unreachable, // TODO - .AsmInput => unreachable, // TODO - //.Asm => { - // const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); - - // try renderToken(ais, tree, asm_node.asm_token, Space.Space); // asm - - // if (asm_node.volatile_token) |volatile_token| { - // try renderToken(ais, tree, volatile_token, Space.Space); // volatile - // try renderToken(ais, tree, tree.nextToken(volatile_token), Space.None); // ( - // } else { - // try renderToken(ais, tree, tree.nextToken(asm_node.asm_token), Space.None); // ( - // } - - // asmblk: { - // ais.pushIndent(); - // defer ais.popIndent(); - - // if (asm_node.outputs.len == 0 and asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) { - // try renderExpression(ais, tree, asm_node.template, Space.None); - // break :asmblk; - // } - - // try renderExpression(ais, tree, asm_node.template, Space.Newline); - - // ais.setIndentDelta(asm_indent_delta); - // defer ais.setIndentDelta(indent_delta); - - // const colon1 = tree.nextToken(asm_node.template.lastToken()); - - // const colon2 = if (asm_node.outputs.len == 0) blk: { - // try renderToken(ais, tree, colon1, Space.Newline); // : - - // break :blk tree.nextToken(colon1); - // } else blk: { - // try renderToken(ais, tree, colon1, Space.Space); // : - - // ais.pushIndent(); - // defer ais.popIndent(); - - // for (asm_node.outputs) |*asm_output, i| { - // if (i + 1 < asm_node.outputs.len) { - // const next_asm_output = asm_node.outputs[i + 1]; - // try renderAsmOutput(allocator, ais, tree, asm_output, Space.None); - - // const comma = tree.prevToken(next_asm_output.firstToken()); - // try renderToken(ais, tree, comma, Space.Newline); // , - // try renderExtraNewlineToken(ais, tree, next_asm_output.firstToken()); - // } else if (asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) { - // try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline); - // break :asmblk; - // } else { - // try renderAsmOutput(allocator, ais, tree, asm_output, Space.Newline); - // const comma_or_colon = tree.nextToken(asm_output.lastToken()); - // break :blk switch (tree.token_tags[comma_or_colon]) { - // .Comma => tree.nextToken(comma_or_colon), - // else => comma_or_colon, - // }; - // } - // } - // unreachable; - // }; - - // const colon3 = if (asm_node.inputs.len == 0) blk: { - // try renderToken(ais, tree, colon2, Space.Newline); // : - // break :blk tree.nextToken(colon2); - // } else blk: { - // try renderToken(ais, tree, colon2, Space.Space); // : - // ais.pushIndent(); - // defer ais.popIndent(); - // for (asm_node.inputs) |*asm_input, i| { - // if (i + 1 < asm_node.inputs.len) { - // const next_asm_input = &asm_node.inputs[i + 1]; - // try renderAsmInput(allocator, ais, tree, asm_input, Space.None); - - // const comma = tree.prevToken(next_asm_input.firstToken()); - // try renderToken(ais, tree, comma, Space.Newline); // , - // try renderExtraNewlineToken(ais, tree, next_asm_input.firstToken()); - // } else if (asm_node.clobbers.len == 0) { - // try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline); - // break :asmblk; - // } else { - // try renderAsmInput(allocator, ais, tree, asm_input, Space.Newline); - // const comma_or_colon = tree.nextToken(asm_input.lastToken()); - // break :blk switch (tree.token_tags[comma_or_colon]) { - // .Comma => tree.nextToken(comma_or_colon), - // else => comma_or_colon, - // }; - // } - // } - // unreachable; - // }; - - // try renderToken(ais, tree, colon3, Space.Space); // : - // ais.pushIndent(); - // defer ais.popIndent(); - // for (asm_node.clobbers) |clobber_node, i| { - // if (i + 1 >= asm_node.clobbers.len) { - // try renderExpression(ais, tree, clobber_node, Space.Newline); - // break :asmblk; - // } else { - // try renderExpression(ais, tree, clobber_node, Space.None); - // const comma = tree.nextToken(clobber_node.lastToken()); - // try renderToken(ais, tree, comma, Space.Space); // , - // } - // } - // } - - // return renderToken(ais, tree, asm_node.rparen, space); - //}, + .AsmSimple => return renderAsm(ais, tree, tree.asmSimple(node), space), + .Asm => return renderAsm(ais, tree, tree.asmFull(node), space), .EnumLiteral => { try renderToken(ais, tree, main_tokens[node] - 1, .None); // . @@ -945,6 +835,8 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac .AlignedVarDecl => unreachable, .UsingNamespace => unreachable, .TestDecl => unreachable, + .AsmOutput => unreachable, + .AsmInput => unreachable, } } @@ -952,7 +844,7 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac fn renderArrayType( ais: *Ais, tree: ast.Tree, - array_type: ast.Full.ArrayType, + array_type: ast.full.ArrayType, space: Space, ) Error!void { try renderToken(ais, tree, array_type.ast.lbracket, .None); // lbracket @@ -968,7 +860,7 @@ fn renderArrayType( fn renderPtrType( ais: *Ais, tree: ast.Tree, - ptr_type: ast.Full.PtrType, + ptr_type: ast.full.PtrType, space: Space, ) Error!void { switch (ptr_type.kind) { @@ -1040,7 +932,7 @@ fn renderPtrType( fn renderSlice( ais: *Ais, tree: ast.Tree, - slice: ast.Full.Slice, + slice: ast.full.Slice, space: Space, ) Error!void { const node_tags = tree.nodes.items(.tag); @@ -1072,48 +964,56 @@ fn renderSlice( } fn renderAsmOutput( - allocator: *mem.Allocator, ais: *Ais, tree: ast.Tree, - asm_output: *const ast.Node.Asm.Output, + asm_output: ast.Node.Index, space: Space, ) Error!void { - try ais.writer().writeAll("["); - try renderExpression(ais, tree, asm_output.symbolic_name, Space.None); - try ais.writer().writeAll("] "); - try renderExpression(ais, tree, asm_output.constraint, Space.None); - try ais.writer().writeAll(" ("); + const token_tags = tree.tokens.items(.tag); + const node_tags = tree.nodes.items(.tag); + const main_tokens = tree.nodes.items(.main_token); + const datas = tree.nodes.items(.data); + assert(node_tags[asm_output] == .AsmOutput); + const symbolic_name = main_tokens[asm_output]; - switch (asm_output.kind) { - .Variable => |variable_name| { - try renderExpression(ais, tree, &variable_name.base, Space.None); - }, - .Return => |return_type| { - try ais.writer().writeAll("-> "); - try renderExpression(ais, tree, return_type, Space.None); - }, + try renderToken(ais, tree, symbolic_name - 1, .None); // lbracket + try renderToken(ais, tree, symbolic_name, .None); // ident + try renderToken(ais, tree, symbolic_name + 1, .Space); // rbracket + try renderToken(ais, tree, symbolic_name + 2, .Space); // "constraint" + try renderToken(ais, tree, symbolic_name + 3, .None); // lparen + + if (token_tags[symbolic_name + 4] == .Arrow) { + try renderToken(ais, tree, symbolic_name + 4, .Space); // -> + try renderExpression(ais, tree, datas[asm_output].lhs, Space.None); + return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen + } else { + try renderToken(ais, tree, symbolic_name + 4, .None); // ident + return renderToken(ais, tree, symbolic_name + 5, space); // rparen } - - return renderToken(ais, tree, asm_output.lastToken(), space); // ) } fn renderAsmInput( - allocator: *mem.Allocator, ais: *Ais, tree: ast.Tree, - asm_input: *const ast.Node.Asm.Input, + asm_input: ast.Node.Index, space: Space, ) Error!void { - try ais.writer().writeAll("["); - try renderExpression(ais, tree, asm_input.symbolic_name, Space.None); - try ais.writer().writeAll("] "); - try renderExpression(ais, tree, asm_input.constraint, Space.None); - try ais.writer().writeAll(" ("); - try renderExpression(ais, tree, asm_input.expr, Space.None); - return renderToken(ais, tree, asm_input.lastToken(), space); // ) + const node_tags = tree.nodes.items(.tag); + const main_tokens = tree.nodes.items(.main_token); + const datas = tree.nodes.items(.data); + assert(node_tags[asm_input] == .AsmInput); + const symbolic_name = main_tokens[asm_input]; + + try renderToken(ais, tree, symbolic_name - 1, .None); // lbracket + try renderToken(ais, tree, symbolic_name, .None); // ident + try renderToken(ais, tree, symbolic_name + 1, .Space); // rbracket + try renderToken(ais, tree, symbolic_name + 2, .Space); // "constraint" + try renderToken(ais, tree, symbolic_name + 3, .None); // lparen + try renderExpression(ais, tree, datas[asm_input].lhs, Space.None); + return renderToken(ais, tree, datas[asm_input].rhs, space); // rparen } -fn renderVarDecl(ais: *Ais, tree: ast.Tree, var_decl: ast.Full.VarDecl) Error!void { +fn renderVarDecl(ais: *Ais, tree: ast.Tree, var_decl: ast.full.VarDecl) Error!void { if (var_decl.visib_token) |visib_token| { try renderToken(ais, tree, visib_token, Space.Space); // pub } @@ -1200,7 +1100,7 @@ fn renderVarDecl(ais: *Ais, tree: ast.Tree, var_decl: ast.Full.VarDecl) Error!vo try renderExpression(ais, tree, var_decl.ast.init_node, .Semicolon); } -fn renderIf(ais: *Ais, tree: ast.Tree, if_node: ast.Full.If, space: Space) Error!void { +fn renderIf(ais: *Ais, tree: ast.Tree, if_node: ast.full.If, space: Space) Error!void { const node_tags = tree.nodes.items(.tag); const token_tags = tree.tokens.items(.tag); @@ -1334,7 +1234,7 @@ fn renderIf(ais: *Ais, tree: ast.Tree, if_node: ast.Full.If, space: Space) Error fn renderContainerField( ais: *Ais, tree: ast.Tree, - field: ast.Full.ContainerField, + field: ast.full.ContainerField, space: Space, ) Error!void { const main_tokens = tree.nodes.items(.main_token); @@ -1430,7 +1330,7 @@ fn renderBuiltinCall( } } -fn renderFnProto(ais: *Ais, tree: ast.Tree, fn_proto: ast.Full.FnProto, space: Space) Error!void { +fn renderFnProto(ais: *Ais, tree: ast.Tree, fn_proto: ast.full.FnProto, space: Space) Error!void { const token_tags = tree.tokens.items(.tag); const token_starts = tree.tokens.items(.start); @@ -1618,7 +1518,7 @@ fn renderFnProto(ais: *Ais, tree: ast.Tree, fn_proto: ast.Full.FnProto, space: S fn renderSwitchCase( ais: *Ais, tree: ast.Tree, - switch_case: ast.Full.SwitchCase, + switch_case: ast.full.SwitchCase, space: Space, ) Error!void { const token_tags = tree.tokens.items(.tag); @@ -1709,7 +1609,7 @@ fn renderBlock( fn renderStructInit( ais: *Ais, tree: ast.Tree, - struct_init: ast.Full.StructInit, + struct_init: ast.full.StructInit, space: Space, ) Error!void { const token_tags = tree.tokens.items(.tag); @@ -1763,7 +1663,7 @@ fn renderStructInit( fn renderArrayInit( ais: *Ais, tree: ast.Tree, - array_init: ast.Full.ArrayInit, + array_init: ast.full.ArrayInit, space: Space, ) Error!void { const token_tags = tree.tokens.items(.tag); @@ -1809,7 +1709,7 @@ fn renderArrayInit( fn renderContainerDecl( ais: *Ais, tree: ast.Tree, - container_decl: ast.Full.ContainerDecl, + container_decl: ast.full.ContainerDecl, space: Space, ) Error!void { const token_tags = tree.tokens.items(.tag); @@ -1894,6 +1794,135 @@ fn renderContainerDecl( return renderToken(ais, tree, rbrace, space); // rbrace } +fn renderAsm( + ais: *Ais, + tree: ast.Tree, + asm_node: ast.full.Asm, + space: Space, +) Error!void { + const token_tags = tree.tokens.items(.tag); + + try renderToken(ais, tree, asm_node.ast.asm_token, .Space); // asm + + if (asm_node.volatile_token) |volatile_token| { + try renderToken(ais, tree, volatile_token, .Space); // volatile + try renderToken(ais, tree, volatile_token + 1, .None); // lparen + } else { + try renderToken(ais, tree, asm_node.ast.asm_token + 1, .None); // lparen + } + + if (asm_node.ast.items.len == 0) { + try renderExpression(ais, tree, asm_node.ast.template, .None); + if (asm_node.first_clobber) |first_clobber| { + // asm ("foo" ::: "a", "b") + var tok_i = first_clobber; + while (true) : (tok_i += 1) { + try renderToken(ais, tree, tok_i, .None); + tok_i += 1; + switch (token_tags[tok_i]) { + .RParen => return renderToken(ais, tree, tok_i, space), + .Comma => try renderToken(ais, tree, tok_i, .Space), + else => unreachable, + } + } + } else { + // asm ("foo") + return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen + } + } + + ais.pushIndent(); + try renderExpression(ais, tree, asm_node.ast.template, .Newline); + ais.setIndentDelta(asm_indent_delta); + const colon1 = tree.lastToken(asm_node.ast.template) + 1; + + const colon2 = if (asm_node.outputs.len == 0) colon2: { + try renderToken(ais, tree, colon1, .Newline); // : + break :colon2 colon1 + 1; + } else colon2: { + try renderToken(ais, tree, colon1, .Space); // : + + ais.pushIndent(); + for (asm_node.outputs) |asm_output, i| { + if (i + 1 < asm_node.outputs.len) { + const next_asm_output = asm_node.outputs[i + 1]; + try renderAsmOutput(ais, tree, asm_output, .None); + + const comma = tree.firstToken(next_asm_output) - 1; + try renderToken(ais, tree, comma, .Newline); // , + try renderExtraNewlineToken(ais, tree, tree.firstToken(next_asm_output)); + } else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) { + try renderAsmOutput(ais, tree, asm_output, .Newline); + ais.popIndent(); + ais.setIndentDelta(indent_delta); + ais.popIndent(); + return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen + } else { + try renderAsmOutput(ais, tree, asm_output, .Newline); + const comma_or_colon = tree.lastToken(asm_output) + 1; + ais.popIndent(); + break :colon2 switch (token_tags[comma_or_colon]) { + .Comma => comma_or_colon + 1, + else => comma_or_colon, + }; + } + } else unreachable; + }; + + const colon3 = if (asm_node.inputs.len == 0) colon3: { + try renderToken(ais, tree, colon2, .Newline); // : + break :colon3 colon2 + 1; + } else colon3: { + try renderToken(ais, tree, colon2, .Space); // : + ais.pushIndent(); + for (asm_node.inputs) |asm_input, i| { + if (i + 1 < asm_node.inputs.len) { + const next_asm_input = asm_node.inputs[i + 1]; + try renderAsmInput(ais, tree, asm_input, .None); + + const first_token = tree.firstToken(next_asm_input); + try renderToken(ais, tree, first_token - 1, .Newline); // , + try renderExtraNewlineToken(ais, tree, first_token); + } else if (asm_node.first_clobber == null) { + try renderAsmInput(ais, tree, asm_input, .Newline); + ais.popIndent(); + ais.setIndentDelta(indent_delta); + ais.popIndent(); + return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen + } else { + try renderAsmInput(ais, tree, asm_input, .Newline); + const comma_or_colon = tree.lastToken(asm_input) + 1; + ais.popIndent(); + break :colon3 switch (token_tags[comma_or_colon]) { + .Comma => comma_or_colon + 1, + else => comma_or_colon, + }; + } + } + unreachable; + }; + + try renderToken(ais, tree, colon3, .Space); // : + const first_clobber = asm_node.first_clobber.?; + var tok_i = first_clobber; + while (true) { + switch (token_tags[tok_i + 1]) { + .RParen => { + ais.setIndentDelta(indent_delta); + ais.popIndent(); + try renderToken(ais, tree, tok_i, .Newline); + return renderToken(ais, tree, tok_i + 1, space); + }, + .Comma => { + try renderToken(ais, tree, tok_i, .None); + try renderToken(ais, tree, tok_i + 1, .Space); + tok_i += 2; + }, + else => unreachable, + } + } else unreachable; // TODO shouldn't need this on while(true) +} + /// Render an expression, and the comma that follows it, if it is present in the source. fn renderExpressionComma(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void { const token_tags = tree.tokens.items(.tag);