159 lines
5.1 KiB
Zig
159 lines
5.1 KiB
Zig
const std = @import("std");
|
|
const sx = @import("sx");
|
|
|
|
pub fn main(init: std.process.Init) !void {
|
|
const allocator = init.arena.allocator();
|
|
const io = init.io;
|
|
const args = try init.minimal.args.toSlice(allocator);
|
|
|
|
if (args.len < 2) {
|
|
printUsage();
|
|
return;
|
|
}
|
|
|
|
const command = args[1];
|
|
|
|
// LSP subcommand doesn't need a file argument
|
|
if (std.mem.eql(u8, command, "lsp")) {
|
|
runLsp(allocator, io);
|
|
return;
|
|
}
|
|
|
|
if (args.len < 3) {
|
|
printUsage();
|
|
return;
|
|
}
|
|
|
|
const input_path = args[2];
|
|
|
|
if (std.mem.eql(u8, command, "build")) {
|
|
const output_name = deriveOutputName(input_path);
|
|
compile(allocator, io, input_path, output_name) catch return;
|
|
std.debug.print("compiled: {s}\n", .{output_name});
|
|
} else if (std.mem.eql(u8, command, "ir")) {
|
|
emitIR(allocator, io, input_path) catch return;
|
|
} else if (std.mem.eql(u8, command, "run")) {
|
|
const tmp_bin = "/tmp/sx_run_tmp";
|
|
compile(allocator, io, input_path, tmp_bin) catch return;
|
|
defer {
|
|
std.Io.Dir.deleteFile(.cwd(), io, tmp_bin) catch {};
|
|
}
|
|
var child = std.process.spawn(io, .{
|
|
.argv = &.{tmp_bin},
|
|
}) catch {
|
|
std.debug.print("error: failed to run program\n", .{});
|
|
return;
|
|
};
|
|
_ = child.wait(io) catch {
|
|
std.debug.print("error: program execution failed\n", .{});
|
|
return;
|
|
};
|
|
} else {
|
|
printUsage();
|
|
}
|
|
}
|
|
|
|
fn printUsage() void {
|
|
std.debug.print(
|
|
\\Usage: sx <command> [file.sx]
|
|
\\
|
|
\\Commands:
|
|
\\ run Build and run immediately
|
|
\\ build Build binary in current directory
|
|
\\ ir Print LLVM IR to stdout
|
|
\\ lsp Start language server (LSP)
|
|
\\
|
|
, .{});
|
|
}
|
|
|
|
fn runLsp(allocator: std.mem.Allocator, io: std.Io) void {
|
|
const Transport = sx.lsp.transport.Transport;
|
|
const Server = sx.lsp.server.Server;
|
|
|
|
const stdin_file = std.Io.File.stdin();
|
|
const stdout_file = std.Io.File.stdout();
|
|
|
|
var read_buf: [4096]u8 = undefined;
|
|
var stdin_reader = stdin_file.readerStreaming(io, &read_buf);
|
|
|
|
var transport = Transport.init(allocator, io, &stdin_reader.interface, stdout_file);
|
|
var server = Server.init(allocator, &transport, io);
|
|
|
|
while (true) {
|
|
const msg = transport.readMessage() catch |err| {
|
|
if (err == error.EndOfStream) break;
|
|
std.debug.print("lsp: read error: {}\n", .{err});
|
|
break;
|
|
};
|
|
|
|
const keep_going = server.handleMessage(msg);
|
|
|
|
if (!keep_going) break;
|
|
}
|
|
}
|
|
|
|
fn deriveOutputName(input_path: []const u8) []const u8 {
|
|
// Get basename (strip directory)
|
|
var start: usize = 0;
|
|
for (input_path, 0..) |ch, i| {
|
|
if (ch == '/') start = i + 1;
|
|
}
|
|
const basename = input_path[start..];
|
|
// Strip .sx extension
|
|
if (std.mem.endsWith(u8, basename, ".sx")) {
|
|
return basename[0 .. basename.len - 3];
|
|
}
|
|
return basename;
|
|
}
|
|
|
|
|
|
fn readSource(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8) ![:0]const u8 {
|
|
const source_bytes = std.Io.Dir.readFileAlloc(.cwd(), io, input_path, allocator, .limited(10 * 1024 * 1024)) catch |err| {
|
|
std.debug.print("error: cannot read '{s}': {}\n", .{ input_path, err });
|
|
return error.CompileError;
|
|
};
|
|
return try allocator.dupeZ(u8, source_bytes);
|
|
}
|
|
|
|
fn emitIR(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8) !void {
|
|
const source = try readSource(allocator, io, input_path);
|
|
|
|
var comp = sx.core.Compilation.init(allocator, io, input_path, source);
|
|
defer comp.deinit();
|
|
|
|
comp.parse() catch { comp.renderErrors(); return error.CompileError; };
|
|
comp.resolveImports() catch { comp.renderErrors(); return error.CompileError; };
|
|
comp.generateCode() catch { comp.renderErrors(); return error.CompileError; };
|
|
|
|
var cg = &comp.cg.?;
|
|
cg.verify() catch { comp.renderErrors(); return error.CompileError; };
|
|
cg.printIR();
|
|
}
|
|
|
|
fn compile(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8, output_path: []const u8) !void {
|
|
const source = try readSource(allocator, io, input_path);
|
|
|
|
var comp = sx.core.Compilation.init(allocator, io, input_path, source);
|
|
defer comp.deinit();
|
|
|
|
comp.parse() catch { comp.renderErrors(); return error.CompileError; };
|
|
comp.resolveImports() catch { comp.renderErrors(); return error.CompileError; };
|
|
comp.generateCode() catch { comp.renderErrors(); return error.CompileError; };
|
|
|
|
var cg = &comp.cg.?;
|
|
cg.verify() catch { comp.renderErrors(); return error.CompileError; };
|
|
|
|
// Emit object file
|
|
const obj_path = try std.fmt.allocPrintSentinel(allocator, "{s}.o", .{output_path}, 0);
|
|
cg.emitObject(obj_path.ptr) catch { comp.renderErrors(); return error.CompileError; };
|
|
|
|
// Link
|
|
sx.codegen.CodeGen.link(io, obj_path, output_path) catch {
|
|
std.debug.print("error: linking failed\n", .{});
|
|
return error.CompileError;
|
|
};
|
|
|
|
// Clean up object file
|
|
std.Io.Dir.deleteFile(.cwd(), io, obj_path) catch {};
|
|
}
|