Skip to content

Commit

Permalink
bring zig fmt to stage1
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewrk committed Feb 16, 2019
1 parent 77a4e7b commit ba56f36
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 7 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,7 @@ set(ZIG_STD_FILES
"special/compiler_rt/udivmodti4.zig"
"special/compiler_rt/udivti3.zig"
"special/compiler_rt/umodti3.zig"
"special/fmt_runner.zig"
"special/init-exe/build.zig"
"special/init-exe/src/main.zig"
"special/init-lib/build.zig"
Expand Down Expand Up @@ -905,3 +906,7 @@ foreach(file ${ZIG_STD_FILES})
get_filename_component(file_dir "${ZIG_STD_DEST}/${file}" DIRECTORY)
install(FILES "${CMAKE_SOURCE_DIR}/std/${file}" DESTINATION "${file_dir}")
endforeach()

install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/arg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/")
install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/main.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/")
install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/errmsg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/")
6 changes: 3 additions & 3 deletions src-self-hosted/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var stderr_file: os.File = undefined;
var stderr: *io.OutStream(os.File.WriteError) = undefined;
var stdout: *io.OutStream(os.File.WriteError) = undefined;

const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB

const usage =
\\usage: zig [command] [options]
Expand Down Expand Up @@ -510,7 +510,7 @@ fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void {
return buildOutputType(allocator, args, Compilation.Kind.Obj);
}

const usage_fmt =
pub const usage_fmt =
\\usage: zig fmt [file]...
\\
\\ Formats the input files and modifies them in-place.
Expand All @@ -527,7 +527,7 @@ const usage_fmt =
\\
;

const args_fmt_spec = []Flag{
pub const args_fmt_spec = []Flag{
Flag.Bool("--help"),
Flag.Bool("--check"),
Flag.Option("--color", []const []const u8{
Expand Down
36 changes: 32 additions & 4 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ static int print_error_usage(const char *arg0) {
return EXIT_FAILURE;
}

static int print_full_usage(const char *arg0) {
fprintf(stdout,
static int print_full_usage(const char *arg0, FILE *file, int return_code) {
fprintf(file,
"Usage: %s [command] [options]\n"
"\n"
"Commands:\n"
Expand All @@ -31,6 +31,7 @@ static int print_full_usage(const char *arg0) {
" build-lib [source] create library from source or object files\n"
" build-obj [source] create object from source or assembly\n"
" builtin show the source code of that @import(\"builtin\")\n"
" fmt parse files and render in canonical zig format\n"
" help show this usage information\n"
" id print the base64-encoded compiler id\n"
" init-exe initialize a `zig build` application in the cwd\n"
Expand Down Expand Up @@ -106,7 +107,7 @@ static int print_full_usage(const char *arg0) {
" --test-cmd [arg] specify test execution command one arg at a time\n"
" --test-cmd-bin appends test binary path to test cmd args\n"
, arg0);
return EXIT_SUCCESS;
return return_code;
}

static const char *ZIG_ZEN = "\n"
Expand Down Expand Up @@ -515,6 +516,31 @@ int main(int argc, char **argv) {
fprintf(stderr, "\n");
}
return (term.how == TerminationIdClean) ? term.code : -1;
} else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) {
init_all_targets();
Buf *fmt_runner_path = buf_alloc();
os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path);
CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(),
nullptr);
g->is_single_threaded = true;
codegen_set_out_name(g, buf_create_from_str("fmt"));
g->enable_cache = true;

codegen_build_and_link(g);

ZigList<const char*> args = {0};
for (int i = 2; i < argc; i += 1) {
args.append(argv[i]);
}
args.append(nullptr);
const char *exec_path = buf_ptr(&g->output_file_path);

os_execv(exec_path, args.items);

args.pop();
Termination term;
os_spawn_process(exec_path, args, &term);
return term.code;
}

for (int i = 1; i < argc; i += 1) {
Expand All @@ -527,6 +553,8 @@ int main(int argc, char **argv) {
build_mode = BuildModeSafeRelease;
} else if (strcmp(arg, "--release-small") == 0) {
build_mode = BuildModeSmallRelease;
} else if (strcmp(arg, "--help") == 0) {
return print_full_usage(arg0, stderr, EXIT_FAILURE);
} else if (strcmp(arg, "--strip") == 0) {
strip = true;
} else if (strcmp(arg, "--static") == 0) {
Expand Down Expand Up @@ -1080,7 +1108,7 @@ int main(int argc, char **argv) {
}
}
case CmdHelp:
return print_full_usage(arg0);
return print_full_usage(arg0, stdout, EXIT_SUCCESS);
case CmdVersion:
printf("%s\n", ZIG_VERSION_STRING);
return EXIT_SUCCESS;
Expand Down
265 changes: 265 additions & 0 deletions std/special/fmt_runner.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
const std = @import("std");
const builtin = @import("builtin");

const os = std.os;
const io = std.io;
const mem = std.mem;
const Allocator = mem.Allocator;
const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
const ast = std.zig.ast;

const arg = @import("fmt/arg.zig");
const self_hosted_main = @import("fmt/main.zig");
const Args = arg.Args;
const Flag = arg.Flag;
const errmsg = @import("fmt/errmsg.zig");

var stderr_file: os.File = undefined;
var stderr: *io.OutStream(os.File.WriteError) = undefined;
var stdout: *io.OutStream(os.File.WriteError) = undefined;

// This brings `zig fmt` to stage 1.
pub fn main() !void {
// Here we use an ArenaAllocator backed by a DirectAllocator because `zig fmt` is a short-lived,
// one shot program. We don't need to waste time freeing memory and finding places to squish
// bytes into. So we free everything all at once at the very end.
var direct_allocator = std.heap.DirectAllocator.init();
var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator);
const allocator = &arena.allocator;

var stdout_file = try std.io.getStdOut();
var stdout_out_stream = stdout_file.outStream();
stdout = &stdout_out_stream.stream;

stderr_file = try std.io.getStdErr();
var stderr_out_stream = stderr_file.outStream();
stderr = &stderr_out_stream.stream;
const args = try std.os.argsAlloc(allocator);

var flags = try Args.parse(allocator, self_hosted_main.args_fmt_spec, args);
defer flags.deinit();

if (flags.present("help")) {
try stdout.write(self_hosted_main.usage_fmt);
os.exit(0);
}

const color = blk: {
if (flags.single("color")) |color_flag| {
if (mem.eql(u8, color_flag, "auto")) {
break :blk errmsg.Color.Auto;
} else if (mem.eql(u8, color_flag, "on")) {
break :blk errmsg.Color.On;
} else if (mem.eql(u8, color_flag, "off")) {
break :blk errmsg.Color.Off;
} else unreachable;
} else {
break :blk errmsg.Color.Auto;
}
};

if (flags.present("stdin")) {
if (flags.positionals.len != 0) {
try stderr.write("cannot use --stdin with positional arguments\n");
os.exit(1);
}

var stdin_file = try io.getStdIn();
var stdin = stdin_file.inStream();

const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size);
defer allocator.free(source_code);

var tree = std.zig.parse(allocator, source_code) catch |err| {
try stderr.print("error parsing stdin: {}\n", err);
os.exit(1);
};
defer tree.deinit();

var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
try printErrMsgToFile(allocator, parse_error, &tree, "<stdin>", stderr_file, color);
}
if (tree.errors.len != 0) {
os.exit(1);
}
if (flags.present("check")) {
const anything_changed = try std.zig.render(allocator, io.null_out_stream, &tree);
const code = if (anything_changed) u8(1) else u8(0);
os.exit(code);
}

_ = try std.zig.render(allocator, stdout, &tree);
return;
}

if (flags.positionals.len == 0) {
try stderr.write("expected at least one source file argument\n");
os.exit(1);
}

if (flags.positionals.len == 0) {

This comment has been minimized.

Copy link
@tiehuis

tiehuis Feb 16, 2019

Member

Redundant check here.

This comment has been minimized.

Copy link
@andrewrk

andrewrk Feb 16, 2019

Author Member

Thanks!

try stderr.write("expected at least one source file argument\n");
os.exit(1);
}

var fmt = Fmt{
.seen = Fmt.SeenMap.init(allocator),
.any_error = false,
.color = color,
.allocator = allocator,
};

const check_mode = flags.present("check");

for (flags.positionals.toSliceConst()) |file_path| {
try fmtPath(&fmt, file_path, check_mode);
}
if (fmt.any_error) {
os.exit(1);
}
}

const FmtError = error{
SystemResources,
OperationAborted,
IoPending,
BrokenPipe,
Unexpected,
WouldBlock,
FileClosed,
DestinationAddressRequired,
DiskQuota,
FileTooBig,
InputOutput,
NoSpaceLeft,
AccessDenied,
OutOfMemory,
RenameAcrossMountPoints,
ReadOnlyFileSystem,
LinkQuotaExceeded,
FileBusy,
} || os.File.OpenError;

fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void {
const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref);
defer fmt.allocator.free(file_path);

if (try fmt.seen.put(file_path, {})) |_| return;

const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {
error.IsDir, error.AccessDenied => {
// TODO make event based (and dir.next())
var dir = try std.os.Dir.open(fmt.allocator, file_path);
defer dir.close();

while (try dir.next()) |entry| {
if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
const full_path = try os.path.join(fmt.allocator, [][]const u8{ file_path, entry.name });
try fmtPath(fmt, full_path, check_mode);
}
}
return;
},
else => {
// TODO lock stderr printing
try stderr.print("unable to open '{}': {}\n", file_path, err);
fmt.any_error = true;
return;
},
};
defer fmt.allocator.free(source_code);

var tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
try stderr.print("error parsing file '{}': {}\n", file_path, err);
fmt.any_error = true;
return;
};
defer tree.deinit();

var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
try printErrMsgToFile(fmt.allocator, parse_error, &tree, file_path, stderr_file, fmt.color);
}
if (tree.errors.len != 0) {
fmt.any_error = true;
return;
}

if (check_mode) {
const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, &tree);
if (anything_changed) {
try stderr.print("{}\n", file_path);
fmt.any_error = true;
}
} else {
// TODO make this evented
const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path);
defer baf.destroy();

const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), &tree);
if (anything_changed) {
try stderr.print("{}\n", file_path);
try baf.finish();
}
}
}

const Fmt = struct {
seen: SeenMap,
any_error: bool,
color: errmsg.Color,
allocator: *mem.Allocator,

const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
};

fn printErrMsgToFile(allocator: *mem.Allocator, parse_error: *const ast.Error, tree: *ast.Tree,
path: []const u8, file: os.File, color: errmsg.Color,) !void
{
const color_on = switch (color) {
errmsg.Color.Auto => file.isTty(),
errmsg.Color.On => true,
errmsg.Color.Off => false,
};
const lok_token = parse_error.loc();
const span = errmsg.Span{
.first = lok_token,
.last = lok_token,
};

const first_token = tree.tokens.at(span.first);
const last_token = tree.tokens.at(span.last);
const start_loc = tree.tokenLocationPtr(0, first_token);
const end_loc = tree.tokenLocationPtr(first_token.end, last_token);

var text_buf = try std.Buffer.initSize(allocator, 0);
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
try parse_error.render(&tree.tokens, out_stream);
const text = text_buf.toOwnedSlice();

const stream = &file.outStream().stream;
if (!color_on) {
try stream.print(
"{}:{}:{}: error: {}\n",
path,
start_loc.line + 1,
start_loc.column + 1,
text,
);
return;
}

try stream.print(
"{}:{}:{}: error: {}\n{}\n",
path,
start_loc.line + 1,
start_loc.column + 1,
text,
tree.source[start_loc.line_start..start_loc.line_end],
);
try stream.writeByteNTimes(' ', start_loc.column);
try stream.writeByteNTimes('~', last_token.end - first_token.start);
try stream.write("\n");
}

0 comments on commit ba56f36

Please sign in to comment.