Skip to content

Commit

Permalink
Merge pull request #7571 from roc-lang/zig-cli
Browse files Browse the repository at this point in the history
Implement an initial roc cli using zig
  • Loading branch information
lukewilliamboswell authored Feb 3, 2025
2 parents 9ea4b10 + d8b3fde commit 015e30b
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 0 deletions.
34 changes: 34 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

const main_path = b.path("src/main.zig");

const exe = b.addExecutable(.{
.name = "roc",
.root_source_file = main_path,
.target = target,
.optimize = optimize,
});

const main_tests = b.addTest(.{ .root_source_file = main_path });
const test_cmd = b.addRunArtifact(main_tests);

const test_step = b.step("test", "Run tests");
test_step.dependOn(&test_cmd.step);

b.installArtifact(exe);

const run_cmd = b.addRunArtifact(exe);

run_cmd.step.dependOn(b.getInstallStep());

if (b.args) |args| {
run_cmd.addArgs(args);
}

const run_step = b.step("run", "Build and run the roc cli");
run_step.dependOn(&run_cmd.step);
}
13 changes: 13 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.{
.name = "roc",
.version = "0.0.0",
.minimum_zig_version = "0.13.0",
.dependencies = .{},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
"LICENSE",
"LEGAL_DETAILS",
},
}
170 changes: 170 additions & 0 deletions src/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
const std = @import("std");
const mem = std.mem;
const Allocator = std.mem.Allocator;

const usage =
\\Usage:
\\
\\ roc [options] [roc_file] [args]
\\ roc [command] [options]
\\
\\Commands:
\\
\\ build Build a binary from the given .roc file, but don't run it
\\ test Run all top-level `expect`s in a main module and any modules it imports
\\ repl Launch the interactive Read Eval Print Loop (REPL)
\\ format Format a .roc file or the .roc files contained in a directory using standard Roc formatting
\\ version Print the Roc compiler’s version, which is currently built from commit 90db3b2db0, committed at 2025-01-28 18:26:51 UTC
\\ check Check the code for problems, but don’t build or run it
\\ docs Generate documentation for a Roc package
\\ glue Generate glue code between a platform's Roc API and its host language
\\
\\General Options:
\\
\\ -h, --help Print command-specific usage
;

pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
std.log.err(format, args);
std.process.exit(1);
}

const RocCmd = enum {
roc_build,
roc_test,
roc_repl,
roc_format,
roc_version,
roc_check,
roc_docs,
roc_glue,
roc_help,

// Parse from string, return null if not found
pub fn fromString(str: []const u8) ?RocCmd {
inline for (std.meta.fields(RocCmd)) |field| {
if (mem.eql(u8, str, field.name)) {
return @enumFromInt(field.value);
}
}
return null;
}

// Define the function type for command handlers
const CommandFn = *const fn (allocator: Allocator, args: []const []const u8) anyerror!void;

const table = std.static_string_map.StaticStringMap(CommandFn).initComptime(.{
.{ "build", rocBuild },
.{ "test", rocTest },
.{ "repl", rocRepl },
.{ "format", rocFormat },
.{ "version", rocVersion },
.{ "check", rocCheck },
.{ "docs", rocDocs },
.{ "glue", rocGlue },
.{ "help", rocHelp },
});
};

pub fn main() !void {
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
defer {
_ = general_purpose_allocator.deinit();
}
const gpa = general_purpose_allocator.allocator();

var arena_instance = std.heap.ArenaAllocator.init(gpa);
defer arena_instance.deinit();
const arena = arena_instance.allocator();

const args = try std.process.argsAlloc(arena);

return mainArgs(gpa, arena, args);
}

fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
_ = gpa;

if (args.len <= 1) {
std.log.info("{s}", .{usage});
fatal("expected command argument", .{});
}

const cmd = args[1];
const cmd_args = args[2..];

if (RocCmd.table.get(cmd)) |handler| {
try handler(arena, cmd_args);
} else if (std.fs.path.extension(cmd).len > 0) {
if (!mem.eql(u8, std.fs.path.extension(cmd), ".roc")) {
fatal("expected .roc file, got: {s}", .{cmd});
}
// Handle .roc file execution
fatal("TODO: run .roc file: {s}", .{cmd});
} else if (mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
try rocHelp(arena, cmd_args);
} else {
std.log.info("{s}", .{usage});
fatal("unknown command: {s}", .{cmd});
}
}

fn print_help() !void {
try std.io.getStdOut().writeAll(usage);
std.process.exit(0);
}

fn rocBuild(allocator: Allocator, args: []const []const u8) !void {
_ = allocator;
_ = args;
fatal("TODO roc build", .{});
}

fn rocTest(allocator: Allocator, args: []const []const u8) !void {
_ = allocator;
_ = args;
fatal("TODO roc test", .{});
}

fn rocRepl(allocator: Allocator, args: []const []const u8) !void {
_ = allocator;
_ = args;
fatal("TODO roc repl", .{});
}

fn rocFormat(allocator: Allocator, args: []const []const u8) !void {
_ = allocator;
_ = args;
fatal("TODO roc format", .{});
}

fn rocVersion(allocator: Allocator, args: []const []const u8) !void {
_ = allocator;
_ = args;
fatal("TODO roc version", .{});
}

fn rocCheck(allocator: Allocator, args: []const []const u8) !void {
_ = allocator;
_ = args;
fatal("TODO roc check", .{});
}

fn rocDocs(allocator: Allocator, args: []const []const u8) !void {
_ = allocator;
_ = args;
fatal("TODO roc docs", .{});
}

fn rocGlue(allocator: Allocator, args: []const []const u8) !void {
_ = allocator;
_ = args;
fatal("TODO roc glue", .{});
}

fn rocHelp(allocator: Allocator, args: []const []const u8) !void {
_ = allocator;
_ = args;

try std.io.getStdOut().writeAll(usage);
}

0 comments on commit 015e30b

Please sign in to comment.