Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement an initial roc cli using zig #7571

Merged
merged 3 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
Loading