| ... | @@ -40,18 +40,22 @@ const Fmt = struct { | ... | @@ -40,18 +40,22 @@ const Fmt = struct { |
| 40 | io: Io, | 40 | io: Io, |
| 41 | out_buffer: std.Io.Writer.Allocating, | 41 | out_buffer: std.Io.Writer.Allocating, |
| 42 | stdout_writer: *Io.File.Writer, | 42 | stdout_writer: *Io.File.Writer, |
| | 43 | stats: ?*Stats, |
| | 44 | |
| | 45 | const SeenMap = std.AutoHashMap(Io.File.INode, void); |
| | 46 | }; |
| | 47 | |
| | 48 | const Stats = struct { |
| 43 | serializer: Serializer, | 49 | serializer: Serializer, |
| 44 | root_struct: Serializer.Struct, | 50 | root_struct: Serializer.Struct, |
| 45 | | 51 | |
| 46 | stats: bool, | 52 | files: u32, |
| 47 | total_tokens: u64, | 53 | tokens: u64, |
| 48 | total_nodes: u64, | 54 | nodes: u64, |
| 49 | /// Count per token tag index | 55 | /// Count per token tag index |
| 50 | token_stats: []u64, | 56 | token_stats: []u64, |
| 51 | /// Count per node tag index | 57 | /// Count per node tag index |
| 52 | node_stats: []u64, | 58 | node_stats: []u64, |
| 53 | | | |
| 54 | const SeenMap = std.AutoHashMap(Io.File.INode, void); | | |
| 55 | }; | 59 | }; |
| 56 | | 60 | |
| 57 | pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void { | 61 | pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void { |
| ... | @@ -60,7 +64,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! | ... | @@ -60,7 +64,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! |
| 60 | var check_flag = false; | 64 | var check_flag = false; |
| 61 | var check_ast_flag = false; | 65 | var check_ast_flag = false; |
| 62 | var force_zon = false; | 66 | var force_zon = false; |
| 63 | var stats = false; | 67 | var enable_stats = false; |
| 64 | | 68 | |
| 65 | var input_files = std.array_list.Managed([]const u8).init(gpa); | 69 | var input_files = std.array_list.Managed([]const u8).init(gpa); |
| 66 | defer input_files.deinit(); | 70 | defer input_files.deinit(); |
| ... | @@ -92,7 +96,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! | ... | @@ -92,7 +96,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! |
| 92 | } else if (mem.eql(u8, arg, "--ast-check")) { | 96 | } else if (mem.eql(u8, arg, "--ast-check")) { |
| 93 | check_ast_flag = true; | 97 | check_ast_flag = true; |
| 94 | } else if (mem.eql(u8, arg, "--stats")) { | 98 | } else if (mem.eql(u8, arg, "--stats")) { |
| 95 | stats = true; | 99 | enable_stats = true; |
| 96 | } else if (mem.eql(u8, arg, "--exclude")) { | 100 | } else if (mem.eql(u8, arg, "--exclude")) { |
| 97 | if (i + 1 >= args.len) { | 101 | if (i + 1 >= args.len) { |
| 98 | fatal("expected parameter after --exclude", .{}); | 102 | fatal("expected parameter after --exclude", .{}); |
| ... | @@ -192,21 +196,25 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! | ... | @@ -192,21 +196,25 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! |
| 192 | .color = color, | 196 | .color = color, |
| 193 | .out_buffer = .init(gpa), | 197 | .out_buffer = .init(gpa), |
| 194 | .stdout_writer = &stdout_writer, | 198 | .stdout_writer = &stdout_writer, |
| 195 | .stats = stats, | 199 | .stats = null, |
| 196 | .total_tokens = 0, | | |
| 197 | .total_nodes = 0, | | |
| 198 | .serializer = .{ .writer = &stdout_writer.interface }, | | |
| 199 | .root_struct = undefined, | | |
| 200 | .token_stats = undefined, | | |
| 201 | .node_stats = undefined, | | |
| 202 | }; | 200 | }; |
| 203 | defer fmt.seen.deinit(); | 201 | defer fmt.seen.deinit(); |
| 204 | defer fmt.out_buffer.deinit(); | 202 | defer fmt.out_buffer.deinit(); |
| 205 | | 203 | |
| 206 | if (stats) { | 204 | if (enable_stats) { |
| 207 | fmt.root_struct = try fmt.serializer.beginStruct(.{}); | 205 | const stats = try arena.create(Stats); |
| 208 | fmt.token_stats = try arena.alloc(u64, @typeInfo(std.zig.Token.Tag).@"enum".field_names.len); | 206 | fmt.stats = stats; |
| 209 | fmt.node_stats = try arena.alloc(u64, @typeInfo(std.zig.Ast.Node.Tag).@"enum".field_names.len); | 207 | stats.* = .{ |
| | 208 | .files = 0, |
| | 209 | .tokens = 0, |
| | 210 | .nodes = 0, |
| | 211 | .serializer = .{ .writer = &stdout_writer.interface }, |
| | 212 | .root_struct = try stats.serializer.beginStruct(.{}), |
| | 213 | .token_stats = try arena.alloc(u64, @typeInfo(std.zig.Token.Tag).@"enum".field_names.len), |
| | 214 | .node_stats = try arena.alloc(u64, @typeInfo(std.zig.Ast.Node.Tag).@"enum".field_names.len), |
| | 215 | }; |
| | 216 | @memset(stats.token_stats, 0); |
| | 217 | @memset(stats.node_stats, 0); |
| 210 | } | 218 | } |
| 211 | | 219 | |
| 212 | // Mark any excluded files/directories as already seen, | 220 | // Mark any excluded files/directories as already seen, |
| ... | @@ -229,10 +237,13 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! | ... | @@ -229,10 +237,13 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! |
| 229 | try fmtPath(&fmt, file_path, check_flag, Io.Dir.cwd(), file_path); | 237 | try fmtPath(&fmt, file_path, check_flag, Io.Dir.cwd(), file_path); |
| 230 | } | 238 | } |
| 231 | | 239 | |
| 232 | if (stats) { | 240 | if (fmt.stats) |stats| { |
| 233 | try fmt.root_struct.field("tokens", fmt.total_tokens, .{}); | 241 | if (stats.files > 1) { |
| 234 | try fmt.root_struct.field("nodes", fmt.total_nodes, .{}); | 242 | try stats.root_struct.field("files", stats.files, .{}); |
| 235 | try fmt.root_struct.end(); | 243 | try stats.root_struct.field("tokens", stats.tokens, .{}); |
| | 244 | try stats.root_struct.field("nodes", stats.nodes, .{}); |
| | 245 | } |
| | 246 | try stats.root_struct.end(); |
| 236 | } | 247 | } |
| 237 | | 248 | |
| 238 | try fmt.stdout_writer.flush(); | 249 | try fmt.stdout_writer.flush(); |
| ... | @@ -378,13 +389,73 @@ fn fmtPathFile( | ... | @@ -378,13 +389,73 @@ fn fmtPathFile( |
| 378 | | 389 | |
| 379 | var zon_struct: Serializer.Struct = undefined; | 390 | var zon_struct: Serializer.Struct = undefined; |
| 380 | | 391 | |
| 381 | if (fmt.stats) { | 392 | if (fmt.stats) |stats| { |
| 382 | zon_struct = try fmt.root_struct.beginStructField(file_path, .{}); | 393 | zon_struct = try stats.root_struct.beginStructField(file_path, .{}); |
| 383 | try zon_struct.field("tokens", tree.tokens.len, .{}); | 394 | |
| 384 | try zon_struct.field("nodes", tree.nodes.len, .{}); | 395 | try zon_struct.field("token_count", tree.tokens.len, .{}); |
| | 396 | try zon_struct.field("node_count", tree.nodes.len, .{}); |
| | 397 | |
| | 398 | const token_names = @typeInfo(std.zig.Token.Tag).@"enum".field_names; |
| | 399 | const node_names = @typeInfo(std.zig.Ast.Node.Tag).@"enum".field_names; |
| | 400 | |
| | 401 | var token_stats: [token_names.len]u64 = @splat(0); |
| | 402 | var token_order: [token_names.len]usize = undefined; |
| | 403 | var node_stats: [node_names.len]u64 = @splat(0); |
| | 404 | var node_order: [node_names.len]usize = undefined; |
| | 405 | |
| | 406 | for (tree.tokens.items(.tag)) |tag| { |
| | 407 | stats.token_stats[@backingInt(tag)] += 1; |
| | 408 | token_stats[@backingInt(tag)] += 1; |
| | 409 | } |
| | 410 | |
| | 411 | for (tree.nodes.items(.tag)) |tag| { |
| | 412 | stats.node_stats[@backingInt(tag)] += 1; |
| | 413 | node_stats[@backingInt(tag)] += 1; |
| | 414 | } |
| | 415 | |
| | 416 | const SortContext = struct { |
| | 417 | stats: []const u64, |
| | 418 | order: []usize, |
| | 419 | |
| | 420 | pub fn lessThan(this: @This(), a_index: usize, b_index: usize) bool { |
| | 421 | return this.stats[this.order[b_index]] < this.stats[this.order[a_index]]; |
| | 422 | } |
| | 423 | |
| | 424 | pub fn swap(this: @This(), a_index: usize, b_index: usize) void { |
| | 425 | std.mem.swap(usize, &this.order[a_index], &this.order[b_index]); |
| | 426 | } |
| | 427 | }; |
| | 428 | |
| | 429 | { |
| | 430 | for (&token_order, 0..) |*elem, i| elem.* = i; |
| | 431 | std.mem.sortContext(0, token_names.len, @as(SortContext, .{ |
| | 432 | .stats = &token_stats, |
| | 433 | .order = &token_order, |
| | 434 | })); |
| | 435 | var s = try zon_struct.beginStructField("tokens", .{}); |
| | 436 | for (&token_order) |i| { |
| | 437 | const n = token_stats[i]; |
| | 438 | if (n != 0) try zon_struct.field(token_names[i], n, .{}); |
| | 439 | } |
| | 440 | try s.end(); |
| | 441 | } |
| | 442 | |
| | 443 | { |
| | 444 | for (&node_order, 0..) |*elem, i| elem.* = i; |
| | 445 | std.mem.sortContext(0, node_names.len, @as(SortContext, .{ |
| | 446 | .stats = &node_stats, |
| | 447 | .order = &node_order, |
| | 448 | })); |
| | 449 | var s = try zon_struct.beginStructField("nodes", .{}); |
| | 450 | for (&node_order) |i| { |
| | 451 | const n = node_stats[i]; |
| | 452 | if (n != 0) try zon_struct.field(node_names[i], n, .{}); |
| | 453 | } |
| | 454 | try s.end(); |
| | 455 | } |
| 385 | | 456 | |
| 386 | fmt.total_tokens += tree.tokens.len; | 457 | stats.tokens += tree.tokens.len; |
| 387 | fmt.total_nodes += tree.nodes.len; | 458 | stats.nodes += tree.nodes.len; |
| 388 | } | 459 | } |
| 389 | | 460 | |
| 390 | // As a heuristic, we make enough capacity for the same as the input source. | 461 | // As a heuristic, we make enough capacity for the same as the input source. |
| ... | @@ -404,14 +475,14 @@ fn fmtPathFile( | ... | @@ -404,14 +475,14 @@ fn fmtPathFile( |
| 404 | try af.file.writeStreamingAll(io, fmt.out_buffer.written()); | 475 | try af.file.writeStreamingAll(io, fmt.out_buffer.written()); |
| 405 | try af.replace(io); | 476 | try af.replace(io); |
| 406 | } | 477 | } |
| 407 | if (fmt.stats) { | 478 | if (fmt.stats != null) { |
| 408 | try zon_struct.field("dirty", true, .{}); | 479 | try zon_struct.field("dirty", true, .{}); |
| 409 | } else { | 480 | } else { |
| 410 | try fmt.stdout_writer.interface.print("{s}\n", .{file_path}); | 481 | try fmt.stdout_writer.interface.print("{s}\n", .{file_path}); |
| 411 | } | 482 | } |
| 412 | } | 483 | } |
| 413 | | 484 | |
| 414 | if (fmt.stats) try zon_struct.end(); | 485 | if (fmt.stats != null) try zon_struct.end(); |
| 415 | } | 486 | } |
| 416 | | 487 | |
| 417 | /// Provided for debugging/testing purposes; unused by the compiler. | 488 | /// Provided for debugging/testing purposes; unused by the compiler. |