From d661883a8de7adea4d6d734b4e87dfccb96d5215 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Thu, 23 Jan 2025 12:15:45 +0100 Subject: [PATCH] feat: Windows support --- .github/workflows/ci.yaml | 42 ++++++++++++++++- build.zig | 81 +++++++------------------------- src/Parser.zig | 22 +++++---- src/buzz_api.zig | 2 +- src/lib/buzz_io.zig | 97 +++++++++++++++++++++++++++++--------- src/lib/buzz_os.zig | 99 ++++++++++++++++++++++++++++++--------- src/main.zig | 2 +- src/repl.zig | 53 +++++++++++++++------ 8 files changed, 262 insertions(+), 136 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a9ea5c0c..e8b2aa16 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,10 +1,50 @@ on: push: - branches: [main] + branches: [main, windows] pull_request: branches: [main] jobs: + test-windows: + runs-on: windows-latest + steps: + - name: Checkout project + uses: actions/checkout@v3.0.0 + - name: Checkout submodules + run: git submodule update --init --recursive + - name: Setup nightly Zig + uses: mlugg/setup-zig@v1 + with: + version: master + - name: Build test ffi lib + run: zig build-lib -dynamic tests/utils/foreign.zig && mv libforeign.* tests/utils/ + + - name: Run tests Debug + run: zig build test + - name: Cleanup + run: rm -rf zig-out zig-cache + - name: Run tests Debug with JIT always on + run: zig build -Djit_always_on test + - name: Cleanup + run: rm -rf zig-out zig-cache + + - name: Run tests ReleaseSafe + run: zig build -Doptimize=ReleaseSafe test + - name: Cleanup + run: rm -rf zig-out zig-cache + - name: Run tests ReleaseSafe with JIT always on + run: zig build -Doptimize=ReleaseSafe -Djit_always_on test + - name: Cleanup + run: rm -rf zig-out zig-cache + + - name: Run tests ReleaseFast + run: zig build -Doptimize=ReleaseFast test + - name: Cleanup + run: rm -rf zig-out zig-cache + - name: Run tests ReleaseFast with JIT always on + run: zig build -Doptimize=ReleaseFast -Djit_always_on test + - name: Cleanup + run: rm -rf zig-out zig-cache test-macos: runs-on: macos-latest steps: diff --git a/build.zig b/build.zig index 56bb211a..cbd78f12 100644 --- a/build.zig +++ b/build.zig @@ -95,11 +95,10 @@ const BuildOptions = struct { }; }; -fn getBuzzPrefix(b: *Build) ![]const u8 { - return std.posix.getenv("BUZZ_PATH") orelse std.fs.path.dirname(b.exe_dir).?; -} - pub fn build(b: *Build) !void { + var envMap = try std.process.getEnvMap(b.allocator); + defer envMap.deinit(); + // Check minimum zig version const current_zig = builtin.zig_version; const min_zig = std.SemanticVersion.parse("0.14.0-dev.2851+b074fb7dd") catch return; @@ -116,8 +115,8 @@ pub fn build(b: *Build) !void { .target = target, .version = std.SemanticVersion{ .major = 0, .minor = 5, .patch = 0 }, // Current commit sha - .sha = std.posix.getenv("GIT_SHA") orelse - std.posix.getenv("GITHUB_SHA") orelse std.mem.trim( + .sha = envMap.get("GIT_SHA") orelse + envMap.get("GITHUB_SHA") orelse std.mem.trim( u8, b.run( &.{ @@ -264,22 +263,6 @@ pub fn build(b: *Build) !void { const build_option_module = build_options.step(b); - var sys_libs = std.ArrayList([]const u8).init(b.allocator); - defer sys_libs.deinit(); - var includes = std.ArrayList([]const u8).init(b.allocator); - defer includes.deinit(); - var llibs = std.ArrayList([]const u8).init(b.allocator); - defer llibs.deinit(); - - includes.appendSlice(&[_][]const u8{ - "./vendors/mir", - "./vendors/mimalloc/include", - }) catch unreachable; - - llibs.appendSlice(&[_][]const u8{ - "./vendors/mir", - }) catch unreachable; - const lib_pcre2 = if (!is_wasm) try buildPcre2(b, target, build_mode) else @@ -288,7 +271,7 @@ pub fn build(b: *Build) !void { try buildMimalloc(b, target, build_mode) else null; - const lib_linenoise = if (!is_wasm) + const lib_linenoise = if (!is_wasm and target.result.os.tag != .windows) try buildLinenoise(b, target, build_mode) else null; @@ -337,20 +320,6 @@ pub fn build(b: *Build) !void { } b.step("run", "run buzz").dependOn(&run_exe.step); - for (includes.items) |include| { - exe.addIncludePath(b.path(include)); - exe_check.addIncludePath(b.path(include)); - } - for (llibs.items) |lib| { - exe.addLibraryPath(b.path(lib)); - exe_check.addLibraryPath(b.path(lib)); - } - for (sys_libs.items) |slib| { - // FIXME: if mir is linked as static library (libmir.a), here also need to link libc - // it's better to built it with Zig's build system - exe.linkSystemLibrary(slib); - exe_check.linkSystemLibrary(slib); - } if (build_options.needLibC()) { exe.linkLibC(); exe_check.linkLibC(); @@ -372,15 +341,6 @@ pub fn build(b: *Build) !void { b.installArtifact(lib); - for (includes.items) |include| { - lib.addIncludePath(b.path(include)); - } - for (llibs.items) |llib| { - lib.addLibraryPath(b.path(llib)); - } - for (sys_libs.items) |slib| { - lib.linkSystemLibrary(slib); - } if (build_options.needLibC()) { lib.linkLibC(); } @@ -392,17 +352,21 @@ pub fn build(b: *Build) !void { if (lib_pcre2) |pcre| { lib.linkLibrary(pcre); + exe.linkLibrary(pcre); } if (lib_mimalloc) |mimalloc| { lib.linkLibrary(mimalloc); + exe.linkLibrary(mimalloc); if (lib.root_module.resolved_target.?.result.os.tag == .windows) { lib.linkSystemLibrary("bcrypt"); + exe.linkSystemLibrary("bcrypt"); } } if (lib_mir) |mir| { lib.linkLibrary(mir); + exe.linkLibrary(mir); } // So that JIT compiled function can reference buzz_api @@ -473,15 +437,6 @@ pub fn build(b: *Build) !void { artifact.dest_dir = .{ .custom = "lib/buzz" }; // No need to link anything when building for wasm since everything is static - for (includes.items) |include| { - std_lib.addIncludePath(b.path(include)); - } - for (llibs.items) |llib| { - std_lib.addLibraryPath(b.path(llib)); - } - for (sys_libs.items) |slib| { - std_lib.linkSystemLibrary(slib); - } if (build_options.needLibC()) { std_lib.linkLibC(); } @@ -515,15 +470,6 @@ pub fn build(b: *Build) !void { .target = target, .optimize = build_mode, }); - for (includes.items) |include| { - tests.addIncludePath(b.path(include)); - } - for (llibs.items) |llib| { - tests.addLibraryPath(b.path(llib)); - } - for (sys_libs.items) |slib| { - tests.linkSystemLibrary(slib); - } if (build_options.needLibC()) { tests.linkLibC(); } @@ -544,7 +490,7 @@ pub fn build(b: *Build) !void { const test_step = b.step("test", "Run all the tests"); const run_tests = b.addRunArtifact(tests); run_tests.cwd = b.path("."); - run_tests.setEnvironmentVariable("BUZZ_PATH", try getBuzzPrefix(b)); + run_tests.setEnvironmentVariable("BUZZ_PATH", envMap.get("BUZZ_PATH") orelse std.fs.path.dirname(b.exe_dir).?); run_tests.step.dependOn(install_step); // wait for libraries to be installed test_step.dependOn(&run_tests.step); } @@ -757,5 +703,10 @@ pub fn buildMir(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.O }, ); + if (target.result.os.tag == .windows) { + lib.linkSystemLibrary("kernel32"); + lib.linkSystemLibrary("psapi"); + } + return lib; } diff --git a/src/Parser.zig b/src/Parser.zig index 6412f0e0..68aa1ddb 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -145,9 +145,14 @@ pub fn defaultBuzzPrefix() []const u8 { } var _buzz_path_buffer: [4096]u8 = undefined; -pub fn buzzPrefix() []const u8 { +pub fn buzzPrefix(allocator: std.mem.Allocator) error{OutOfMemory}![]const u8 { // FIXME: don't use std.posix directly - if (std.posix.getenv("BUZZ_PATH")) |buzz_path| { + if (std.process.getEnvVarOwned(allocator, "BUZZ_PATH") catch |err| env: { + switch (err) { + error.EnvironmentVariableNotFound, error.InvalidWtf8 => break :env null, + else => return error.OutOfMemory, + } + }) |buzz_path| { return buzz_path; } @@ -163,8 +168,9 @@ pub fn buzzPrefix() []const u8 { var _buzz_path_buffer2: [4096]u8 = undefined; /// the returned string can be used only until next call to this function -pub fn buzzLibPath() ![]const u8 { - const path2 = buzzPrefix(); +pub fn buzzLibPath(allocator: std.mem.Allocator) ![]const u8 { + const path2 = try buzzPrefix(allocator); + defer allocator.free(path2); const sep = std.fs.path.sep_str; return std.fmt.bufPrint( &_buzz_path_buffer2, @@ -7597,7 +7603,7 @@ fn searchPaths(self: *Self, file_name: []const u8) ![][]const u8 { self.gc.allocator, suffixed, "$", - try buzzLibPath(), + try buzzLibPath(self.gc.allocator), ); try paths.append(prefixed); @@ -7636,7 +7642,7 @@ fn searchLibPaths(self: *Self, file_name: []const u8) !std.ArrayList([]const u8) self.gc.allocator, suffixed, "$", - try buzzLibPath(), + try buzzLibPath(self.gc.allocator), ); try paths.append(prefixed); @@ -8137,7 +8143,7 @@ fn importLibSymbol(self: *Self, full_file_name: []const u8, symbol: []const u8) "External library `{s}` not found: {s}{s}\n", .{ file_basename, - if (builtin.link_libc) + if (builtin.link_libc and builtin.os.tag != .windows) std.mem.sliceTo(dlerror(), 0) else "", @@ -8348,7 +8354,7 @@ fn zdefStatement(self: *Self) Error!Ast.Node.Index { "External library `{s}` not found: {s}{s}\n", .{ lib_name_str, - if (builtin.link_libc) + if (builtin.link_libc and builtin.os.tag != .windows) std.mem.sliceTo(dlerror(), 0) else "", diff --git a/src/buzz_api.zig b/src/buzz_api.zig index b3971e65..4fded863 100644 --- a/src/buzz_api.zig +++ b/src/buzz_api.zig @@ -1127,7 +1127,7 @@ export fn bz_rethrow(vm: *VM) void { if ((vm.currentFrame() == null or vm.currentFrame().?.in_native_call) and vm.current_fiber.try_context != null) { // FIXME: close try scope - if (builtin.os.tag == .macos or builtin.os.tag == .linux or builtin.os.windows) { + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { jmp._longjmp(&vm.current_fiber.try_context.?.env, 1); } else { jmp.longjmp(&vm.current_fiber.try_context.?.env, 1); diff --git a/src/lib/buzz_io.zig b/src/lib/buzz_io.zig index 76744176..cb54064d 100644 --- a/src/lib/buzz_io.zig +++ b/src/lib/buzz_io.zig @@ -1,29 +1,55 @@ const std = @import("std"); const api = @import("buzz_api.zig"); const io = @import("io.zig"); +const builtin = @import("builtin"); pub export fn getStdIn(ctx: *api.NativeCtx) c_int { - ctx.vm.bz_push(api.Value.fromInteger(@intCast(std.io.getStdIn().handle))); + ctx.vm.bz_push( + api.Value.fromInteger( + if (builtin.os.tag == .windows) + @intCast(@intFromPtr(std.io.getStdIn().handle)) + else + @intCast(std.io.getStdIn().handle), + ), + ); return 1; } pub export fn getStdOut(ctx: *api.NativeCtx) c_int { - ctx.vm.bz_push(api.Value.fromInteger(@intCast(std.io.getStdOut().handle))); + ctx.vm.bz_push( + api.Value.fromInteger( + if (builtin.os.tag == .windows) + @intCast(@intFromPtr(std.io.getStdOut().handle)) + else + @intCast(std.io.getStdOut().handle), + ), + ); return 1; } pub export fn getStdErr(ctx: *api.NativeCtx) c_int { - ctx.vm.bz_push(api.Value.fromInteger(@intCast(std.io.getStdErr().handle))); + ctx.vm.bz_push( + api.Value.fromInteger( + if (builtin.os.tag == .windows) + @intCast(@intFromPtr(std.io.getStdErr().handle)) + else + @intCast(std.io.getStdErr().handle), + ), + ); return 1; } pub export fn FileIsTTY(ctx: api.NativeCtx) c_int { - const handle: std.fs.File.Handle = @intCast( - ctx.vm.bz_peek(0).integer(), - ); + const handle: std.fs.File.Handle = + if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(0).integer()))) + else + @intCast( + ctx.vm.bz_peek(0).integer(), + ); ctx.vm.bz_push(api.Value.fromBoolean(std.posix.isatty(handle))); @@ -94,15 +120,26 @@ pub export fn FileOpen(ctx: *api.NativeCtx) c_int { }, }; - ctx.vm.bz_push(api.Value.fromInteger(@intCast(file.handle))); + ctx.vm.bz_push( + api.Value.fromInteger( + if (builtin.os.tag == .windows) + @intCast(@intFromPtr(file.handle)) + else + @intCast(file.handle), + ), + ); return 1; } pub export fn FileClose(ctx: *api.NativeCtx) c_int { - const handle: std.fs.File.Handle = @intCast( - ctx.vm.bz_peek(0).integer(), - ); + const handle: std.fs.File.Handle = + if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(0).integer()))) + else + @intCast( + ctx.vm.bz_peek(0).integer(), + ); const file: std.fs.File = std.fs.File{ .handle = handle }; file.close(); @@ -144,9 +181,13 @@ fn handleFileReadWriteError(ctx: *api.NativeCtx, err: anytype) void { } pub export fn FileReadAll(ctx: *api.NativeCtx) c_int { - const handle: std.fs.File.Handle = @intCast( - ctx.vm.bz_peek(1).integer(), - ); + const handle: std.fs.File.Handle = + if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(0).integer()))) + else + @intCast( + ctx.vm.bz_peek(0).integer(), + ); const max_size = ctx.vm.bz_peek(0); const file: std.fs.File = std.fs.File{ .handle = handle }; @@ -212,9 +253,13 @@ fn handleFileReadLineError(ctx: *api.NativeCtx, err: anytype) void { } pub export fn FileReadLine(ctx: *api.NativeCtx) c_int { - const handle: std.fs.File.Handle = @intCast( - ctx.vm.bz_peek(1).integer(), - ); + const handle: std.fs.File.Handle = + if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(0).integer()))) + else + @intCast( + ctx.vm.bz_peek(0).integer(), + ); const max_size = ctx.vm.bz_peek(0); const file: std.fs.File = std.fs.File{ .handle = handle }; @@ -284,9 +329,13 @@ pub export fn FileRead(ctx: *api.NativeCtx) c_int { return -1; } - const handle: std.fs.File.Handle = @intCast( - ctx.vm.bz_peek(1).integer(), - ); + const handle: std.fs.File.Handle = + if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(1).integer()))) + else + @intCast( + ctx.vm.bz_peek(1).integer(), + ); const file: std.fs.File = std.fs.File{ .handle = handle }; const reader = file.reader(); @@ -322,9 +371,13 @@ pub export fn FileRead(ctx: *api.NativeCtx) c_int { // extern fun File_write(int fd, [int] bytes) > void; pub export fn FileWrite(ctx: *api.NativeCtx) c_int { - const handle: std.fs.File.Handle = @intCast( - ctx.vm.bz_peek(1).integer(), - ); + const handle: std.fs.File.Handle = + if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(1).integer()))) + else + @intCast( + ctx.vm.bz_peek(1).integer(), + ); const file: std.fs.File = std.fs.File{ .handle = handle }; diff --git a/src/lib/buzz_os.zig b/src/lib/buzz_os.zig index fcaf868f..844396ef 100644 --- a/src/lib/buzz_os.zig +++ b/src/lib/buzz_os.zig @@ -31,7 +31,12 @@ pub export fn env(ctx: *api.NativeCtx) c_int { defer api.VM.allocator.free(key_slice); // FIXME: don't use std.posix directly - if (std.posix.getenv(key_slice)) |value| { + if (std.process.getEnvVarOwned(api.VM.allocator, "key_slice") catch |err| env: { + switch (err) { + error.EnvironmentVariableNotFound, error.InvalidWtf8 => break :env null, + else => @panic("Out of memory"), + } + }) |value| { ctx.vm.bz_push( api.VM.bz_stringToValue( ctx.vm, @@ -40,6 +45,8 @@ pub export fn env(ctx: *api.NativeCtx) c_int { ), ); + api.VM.allocator.free(value); + return 1; } @@ -121,7 +128,7 @@ pub export fn tmpFilename(ctx: *api.NativeCtx) c_int { return 1; } -// If it was named `exit` it would be considered by zig as a callback when std.posix.exit is called +// If it was named `exit` it would be considered by zig as a callback when std.process.exit is called pub export fn buzzExit(ctx: *api.NativeCtx) c_int { const exitCode: i32 = ctx.vm.bz_peek(0).integer(); @@ -347,7 +354,14 @@ pub export fn SocketConnect(ctx: *api.NativeCtx) c_int { return -1; }; - ctx.vm.bz_push(api.Value.fromInteger(@intCast(stream.handle))); + ctx.vm.bz_push( + api.Value.fromInteger( + if (builtin.os.tag == .windows) + @intCast(@intFromPtr(stream.handle)) + else + @intCast(stream.handle), + ), + ); return 1; }, @@ -358,6 +372,13 @@ pub export fn SocketConnect(ctx: *api.NativeCtx) c_int { return -1; }, 2 => { + // Unix socket not available on windows + if (builtin.os.tag == .windows) { + ctx.vm.pushError("errors.InvalidArgumentError", null); + + return -1; + } + const stream = std.net.connectUnixSocket(address) catch |err| { handleConnectUnixError(ctx, err); @@ -377,9 +398,12 @@ pub export fn SocketConnect(ctx: *api.NativeCtx) c_int { } pub export fn SocketClose(ctx: *api.NativeCtx) c_int { - const socket: std.posix.socket_t = @intCast( - ctx.vm.bz_peek(0).integer(), - ); + const socket: std.posix.socket_t = if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(0).integer()))) + else + @intCast( + ctx.vm.bz_peek(0).integer(), + ); std.posix.shutdown(socket, .both) catch @panic("Could not stop socket"); @@ -421,9 +445,12 @@ pub export fn SocketRead(ctx: *api.NativeCtx) c_int { return -1; } - const handle: std.posix.socket_t = @intCast( - ctx.vm.bz_peek(1).integer(), - ); + const handle: std.posix.socket_t = if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(1).integer()))) + else + @intCast( + ctx.vm.bz_peek(1).integer(), + ); const stream: std.net.Stream = .{ .handle = handle }; const reader = stream.reader(); @@ -494,9 +521,12 @@ fn handleReadLineError(ctx: *api.NativeCtx, err: anytype) void { } pub export fn SocketReadLine(ctx: *api.NativeCtx) c_int { - const handle: std.posix.socket_t = @intCast( - ctx.vm.bz_peek(1).integer(), - ); + const handle: std.posix.socket_t = if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(1).integer()))) + else + @intCast( + ctx.vm.bz_peek(1).integer(), + ); const max_size = ctx.vm.bz_peek(0); const stream: std.net.Stream = .{ .handle = handle }; @@ -537,9 +567,12 @@ pub export fn SocketReadLine(ctx: *api.NativeCtx) c_int { } pub export fn SocketReadAll(ctx: *api.NativeCtx) c_int { - const handle: std.posix.socket_t = @intCast( - ctx.vm.bz_peek(1).integer(), - ); + const handle: std.posix.socket_t = if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(1).integer()))) + else + @intCast( + ctx.vm.bz_peek(1).integer(), + ); const max_size = ctx.vm.bz_peek(0); const stream: std.net.Stream = .{ .handle = handle }; @@ -581,9 +614,12 @@ pub export fn SocketReadAll(ctx: *api.NativeCtx) c_int { } pub export fn SocketWrite(ctx: *api.NativeCtx) c_int { - const handle: std.posix.socket_t = @intCast( - ctx.vm.bz_peek(1).integer(), - ); + const handle: std.posix.socket_t = if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(1).integer()))) + else + @intCast( + ctx.vm.bz_peek(1).integer(), + ); const stream: std.net.Stream = .{ .handle = handle }; @@ -687,7 +723,14 @@ pub export fn SocketServerStart(ctx: *api.NativeCtx) c_int { return -1; }; - ctx.vm.bz_push(api.Value.fromInteger(@intCast(server.stream.handle))); + ctx.vm.bz_push( + api.Value.fromInteger( + if (builtin.os.tag == .windows) + @intCast(@intFromPtr(server.stream.handle)) + else + @intCast(server.stream.handle), + ), + ); return 1; } @@ -696,9 +739,12 @@ pub export fn SocketServerAccept(ctx: *api.NativeCtx) c_int { var server = std.net.Server{ .listen_address = undefined, // FIXME: we lose this .stream = std.net.Stream{ - .handle = @intCast( - ctx.vm.bz_peek(0).integer(), - ), + .handle = if (builtin.os.tag == .windows) + @ptrFromInt(@as(usize, @intCast(ctx.vm.bz_peek(0).integer()))) + else + @intCast( + ctx.vm.bz_peek(0).integer(), + ), }, }; @@ -723,7 +769,14 @@ pub export fn SocketServerAccept(ctx: *api.NativeCtx) c_int { return -1; }; - ctx.vm.bz_push(api.Value.fromInteger(@intCast(connection.stream.handle))); + ctx.vm.bz_push( + api.Value.fromInteger( + if (builtin.os.tag == .windows) + @intCast(@intFromPtr(connection.stream.handle)) + else + @intCast(connection.stream.handle), + ), + ); return 1; } diff --git a/src/main.zig b/src/main.zig index 7ccb04b8..ca5b5652 100644 --- a/src/main.zig +++ b/src/main.zig @@ -377,7 +377,7 @@ test "Testing behavior" { allocator, "{s}/bin/buzz", .{ - Parser.buzzPrefix(), + try Parser.buzzPrefix(allocator), }, ) catch unreachable; defer allocator.free(arg0); diff --git a/src/repl.zig b/src/repl.zig index 7b4037cf..b2446484 100644 --- a/src/repl.zig +++ b/src/repl.zig @@ -31,7 +31,7 @@ const ObjForeignContainer = _obj.ObjForeignContainer; const Parser = @import("Parser.zig"); const CompileError = Parser.CompileError; const JIT = @import("Jit.zig"); -const ln = @import("linenoise.zig"); +const ln = if (builtin.os.tag != .windows) @import("linenoise.zig") else void; const Value = @import("value.zig").Value; const disassembler = @import("disassembler.zig"); const dumpStack = disassembler.dumpStack; @@ -89,7 +89,9 @@ pub fn printBanner(out: anytype, full: bool) void { } pub fn repl(allocator: std.mem.Allocator) !void { - const colorterm = std.posix.getenv("COLORTERM"); + var envMap = try std.process.getEnvMap(allocator); + defer envMap.deinit(); + const colorterm = envMap.get("COLORTERM"); const true_color = if (colorterm) |ct| std.mem.eql(u8, ct, "24bit") or std.mem.eql(u8, ct, "truecolor") else @@ -144,11 +146,13 @@ pub fn repl(allocator: std.mem.Allocator) !void { try buzz_history_path.writer().print( "{s}/.buzz_history\x00", - .{std.posix.getenv("HOME") orelse "."}, + .{envMap.get("HOME") orelse "."}, ); - _ = ln.linenoiseHistorySetMaxLen(100); - _ = ln.linenoiseHistoryLoad(@ptrCast(buzz_history_path.items.ptr)); + if (builtin.os.tag != .windows) { + _ = ln.linenoiseHistorySetMaxLen(100); + _ = ln.linenoiseHistoryLoad(@ptrCast(buzz_history_path.items.ptr)); + } // Import std and debug as commodity _ = runSource( @@ -165,20 +169,37 @@ pub fn repl(allocator: std.mem.Allocator) !void { var previous_globals = try vm.globals.clone(); var previous_type_registry = try gc.type_registry.registry.clone(); var previous_input: ?[]u8 = null; + const stdin_buffer = if (builtin.os.tag == .windows) + gc.allocator.alloc(u8, 2048) catch @panic("Out of memory") + else + null; while (true) { - const read_source = ln.linenoise( - if (previous_input != null) - MULTILINE_PROMPT - else - PROMPT, - ); + if (builtin.os.tag == .windows) { + std.io.getStdOut().writeAll( + if (previous_input != null) + MULTILINE_PROMPT + else + PROMPT, + ) catch @panic("Could not write to stdout"); + } + + const read_source = if (builtin.os.tag != .windows) + ln.linenoise( + if (previous_input != null) + MULTILINE_PROMPT + else + PROMPT, + ) + else + std.io.getStdIn().reader() + .readUntilDelimiterOrEof(stdin_buffer, '\n') catch @panic("Could not read stdin"); if (read_source == null) { std.process.exit(0); } - var source = std.mem.span(read_source.?); + var source = if (builtin.os.tag == .windows) read_source.? else std.mem.span(read_source.?); const original_source = source; if (source.len > 0) { @@ -232,8 +253,10 @@ pub fn repl(allocator: std.mem.Allocator) !void { }; if (parser.reporter.last_error == null and codegen.reporter.last_error == null) { - _ = ln.linenoiseHistoryAdd(source); - _ = ln.linenoiseHistorySave(@ptrCast(buzz_history_path.items.ptr)); + if (builtin.os.tag != .windows) { + _ = ln.linenoiseHistoryAdd(source); + _ = ln.linenoiseHistorySave(@ptrCast(buzz_history_path.items.ptr)); + } // FIXME: why can't I deinit those? // previous_parser_globals.deinit(); previous_parser_globals = try parser.globals.clone(); @@ -284,7 +307,7 @@ pub fn repl(allocator: std.mem.Allocator) !void { if (parser.reporter.last_error == .unclosed) { previous_input = gc.allocator.alloc(u8, source.len) catch @panic("Out of memory"); std.mem.copyForwards(u8, previous_input.?, source); - } else { + } else if (builtin.os.tag != .windows) { _ = ln.linenoiseHistoryAdd(source); _ = ln.linenoiseHistorySave(@ptrCast(buzz_history_path.items.ptr)); }