Merge remote-tracking branch 'origin/trunk' into editor-ir

This commit is contained in:
Folkert 2020-12-05 20:58:27 +01:00
commit 1fe0f5d780
26 changed files with 785 additions and 460 deletions

26
Cargo.lock generated
View file

@ -270,6 +270,20 @@ name = "bytemuck"
version = "1.4.1" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41aa2ec95ca3b5c54cf73c91acf06d24f4495d5f1b1c12506ae3483d646177ac" checksum = "41aa2ec95ca3b5c54cf73c91acf06d24f4495d5f1b1c12506ae3483d646177ac"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54"
dependencies = [
"proc-macro2 1.0.24",
"quote 1.0.7",
"syn 1.0.53",
]
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -324,6 +338,17 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cgmath"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "283944cdecc44bf0b8dd010ec9af888d3b4f142844fdbe026c20ef68148d6fe7"
dependencies = [
"approx 0.3.2",
"num-traits",
"rand 0.6.5",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.33.3" version = "2.33.3"
@ -2637,6 +2662,7 @@ dependencies = [
"arraystring", "arraystring",
"bumpalo", "bumpalo",
"bytemuck", "bytemuck",
"cgmath",
"env_logger 0.7.1", "env_logger 0.7.1",
"fs_extra", "fs_extra",
"futures", "futures",

1
compiler/builtins/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
builtins.ll

View file

@ -9,16 +9,13 @@ pub fn build(b: *Builder) void {
// Options // Options
const fallback_main_path = "./src/main.zig"; const fallback_main_path = "./src/main.zig";
const main_path_desc = b.fmt("Override path to main.zig. Used by \"ir\", \"bc\", and \"test\". Defaults to \"{}\". ", .{fallback_main_path}); const main_path_desc = b.fmt("Override path to main.zig. Used by \"ir\" and \"test\". Defaults to \"{}\". ", .{fallback_main_path});
const main_path = b.option([]const u8, "main-path", main_path_desc) orelse fallback_main_path; const main_path = b.option([]const u8, "main-path", main_path_desc) orelse fallback_main_path;
const fallback_bitcode_path = "./builtins.bc";
const bitcode_path_desc = b.fmt("Override path to generated bitcode file. Used by \"ir\" and \"bc\". Defaults to \"{}\". ", .{fallback_bitcode_path});
const bitcode_path = b.option([]const u8, "bc-path", bitcode_path_desc) orelse fallback_bitcode_path;
// Tests // Tests
var main_tests = b.addTest(main_path); var main_tests = b.addTest(main_path);
main_tests.setBuildMode(mode); main_tests.setBuildMode(mode);
main_tests.linkSystemLibrary("c");
const test_step = b.step("test", "Run tests"); const test_step = b.step("test", "Run tests");
test_step.dependOn(&main_tests.step); test_step.dependOn(&main_tests.step);
@ -26,25 +23,13 @@ pub fn build(b: *Builder) void {
const obj_name = "builtins"; const obj_name = "builtins";
const obj = b.addObject(obj_name, main_path); const obj = b.addObject(obj_name, main_path);
obj.setBuildMode(mode); obj.setBuildMode(mode);
obj.linkSystemLibrary("c");
obj.strip = true; obj.strip = true;
obj.emit_llvm_ir = true; obj.emit_llvm_ir = true;
obj.emit_bin = false; obj.emit_bin = false;
const ir = b.step("ir", "Build LLVM ir"); const ir = b.step("ir", "Build LLVM ir");
ir.dependOn(&obj.step); ir.dependOn(&obj.step);
// IR to Bitcode
const bitcode_path_arg = b.fmt("-o={}", .{bitcode_path});
const ir_out_file = b.fmt("{}.ll", .{obj_name});
const ir_to_bitcode = b.addSystemCommand(&[_][]const u8{
"llvm-as-10",
ir_out_file,
bitcode_path_arg,
});
const bicode = b.step("bc", "Build LLVM ir and convert to bitcode");
bicode.dependOn(ir);
bicode.dependOn(&ir_to_bitcode.step);
b.default_step = ir; b.default_step = ir;
removeInstallSteps(b); removeInstallSteps(b);
} }

View file

@ -3,7 +3,7 @@
set -euxo pipefail set -euxo pipefail
# Test every zig # Test every zig
find src/*.zig -type f -print0 | xargs -n 1 -0 zig test --library c zig build test
# fmt every zig # fmt every zig
find src/*.zig -type f -print0 | xargs -n 1 -0 zig fmt --check find src/*.zig -type f -print0 | xargs -n 1 -0 zig fmt --check

View file

@ -15,14 +15,14 @@ comptime {
// Str Module // Str Module
const str = @import("str.zig"); const str = @import("str.zig");
comptime { comptime {
exportStrFn(str.strSplitInPlace, "str_split_in_place"); exportStrFn(str.strSplitInPlaceC, "str_split_in_place");
exportStrFn(str.countSegments, "count_segments"); exportStrFn(str.countSegments, "count_segments");
exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters"); exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters");
exportStrFn(str.startsWith, "starts_with"); exportStrFn(str.startsWith, "starts_with");
exportStrFn(str.endsWith, "ends_with"); exportStrFn(str.endsWith, "ends_with");
exportStrFn(str.strConcat, "concat"); exportStrFn(str.strConcatC, "concat");
exportStrFn(str.strNumberOfBytes, "number_of_bytes"); exportStrFn(str.strNumberOfBytes, "number_of_bytes");
exportStrFn(str.strFromInt, "from_int"); exportStrFn(str.strFromIntC, "from_int");
} }
// Export helpers - Must be run inside a comptime // Export helpers - Must be run inside a comptime

View file

@ -1,26 +1,25 @@
const std = @import("std"); const std = @import("std");
const mem = std.mem;
const Allocator = mem.Allocator;
const unicode = std.unicode; const unicode = std.unicode;
const testing = std.testing; const testing = std.testing;
const expectEqual = testing.expectEqual; const expectEqual = testing.expectEqual;
const expect = testing.expect; const expect = testing.expect;
extern fn malloc(size: usize) ?*u8;
extern fn free([*]u8) void;
const RocStr = extern struct { const RocStr = extern struct {
str_bytes: ?[*]u8, str_bytes: ?[*]u8,
str_len: usize, str_len: usize,
pub fn empty() RocStr { pub inline fn empty() RocStr {
return RocStr{ return RocStr{
.str_len = 0, .str_len = 0,
.str_bytes = null, .str_bytes = null,
}; };
} }
// This takes ownership of the pointed-to bytes if they won't fit in a // This clones the pointed-to bytes if they won't fit in a
// small string, and returns a (pointer, len) tuple which points to them. // small string, and returns a (pointer, len) tuple which points to them.
pub fn init(bytes: [*]const u8, length: usize) RocStr { pub fn init(allocator: *Allocator, bytes_ptr: [*]const u8, length: usize) RocStr {
const roc_str_size = @sizeOf(RocStr); const roc_str_size = @sizeOf(RocStr);
if (length < roc_str_size) { if (length < roc_str_size) {
@ -40,7 +39,7 @@ const RocStr = extern struct {
index = 0; index = 0;
while (index < length) { while (index < length) {
var offset_ptr = @intToPtr(*u8, target_ptr + index); var offset_ptr = @intToPtr(*u8, target_ptr + index);
offset_ptr.* = bytes[index]; offset_ptr.* = bytes_ptr[index];
index += 1; index += 1;
} }
@ -50,9 +49,9 @@ const RocStr = extern struct {
return ret_small_str; return ret_small_str;
} else { } else {
var result = allocateStr(u64, InPlace.Clone, length); var result = allocateStr(allocator, u64, InPlace.Clone, length);
@memcpy(@ptrCast([*]u8, result.str_bytes), bytes, length); @memcpy(@ptrCast([*]u8, result.str_bytes), bytes_ptr, length);
return result; return result;
} }
@ -66,20 +65,23 @@ const RocStr = extern struct {
if (length < roc_str_size) { if (length < roc_str_size) {
return RocStr.empty(); return RocStr.empty();
} else { } else {
var new_bytes: [*]u8 = @ptrCast([*]u8, malloc(length)); var new_bytes: []T = allocator.alloc(u8, length) catch unreachable;
var new_bytes_ptr: [*]u8 = @ptrCast([*]u8, &new_bytes);
return RocStr{ return RocStr{
.str_bytes = new_bytes, .str_bytes = new_bytes_ptr,
.str_len = length, .str_len = length,
}; };
} }
} }
pub fn deinit(self: RocStr) void { pub fn deinit(self: RocStr, allocator: *Allocator) void {
if (!self.isSmallStr()) { if (!self.isSmallStr() and !self.isEmpty()) {
const str_bytes: [*]u8 = self.str_bytes orelse unreachable; const str_bytes_ptr: [*]u8 = self.str_bytes orelse unreachable;
free(str_bytes); const str_bytes: []u8 = str_bytes_ptr[0..self.str_len];
allocator.free(str_bytes);
} }
} }
@ -169,70 +171,77 @@ const RocStr = extern struct {
const str1_len = 3; const str1_len = 3;
var str1: [str1_len]u8 = "abc".*; var str1: [str1_len]u8 = "abc".*;
const str1_ptr: [*]u8 = &str1; const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(str1_ptr, str1_len); var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
const str2_len = 3; const str2_len = 3;
var str2: [str2_len]u8 = "abc".*; var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2; const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(str2_ptr, str2_len); var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
// TODO: fix those tests // TODO: fix those tests
// expect(roc_str1.eq(roc_str2)); // expect(roc_str1.eq(roc_str2));
roc_str1.deinit(); roc_str1.deinit(testing.allocator);
roc_str2.deinit(); roc_str2.deinit(testing.allocator);
} }
test "RocStr.eq: not equal different length" { test "RocStr.eq: not equal different length" {
const str1_len = 4; const str1_len = 4;
var str1: [str1_len]u8 = "abcd".*; var str1: [str1_len]u8 = "abcd".*;
const str1_ptr: [*]u8 = &str1; const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(str1_ptr, str1_len); var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
const str2_len = 3; const str2_len = 3;
var str2: [str2_len]u8 = "abc".*; var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2; const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(str2_ptr, str2_len); var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
defer {
roc_str1.deinit(testing.allocator);
roc_str2.deinit(testing.allocator);
}
expect(!roc_str1.eq(roc_str2)); expect(!roc_str1.eq(roc_str2));
roc_str1.deinit();
roc_str2.deinit();
} }
test "RocStr.eq: not equal same length" { test "RocStr.eq: not equal same length" {
const str1_len = 3; const str1_len = 3;
var str1: [str1_len]u8 = "acb".*; var str1: [str1_len]u8 = "acb".*;
const str1_ptr: [*]u8 = &str1; const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(str1_ptr, str1_len); var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
const str2_len = 3; const str2_len = 3;
var str2: [str2_len]u8 = "abc".*; var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2; const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(str2_ptr, str2_len); var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
defer {
roc_str1.deinit(testing.allocator);
roc_str2.deinit(testing.allocator);
}
// TODO: fix those tests // TODO: fix those tests
// expect(!roc_str1.eq(roc_str2)); // expect(!roc_str1.eq(roc_str2));
roc_str1.deinit();
roc_str2.deinit();
} }
}; };
// Str.numberOfBytes // Str.numberOfBytes
pub fn strNumberOfBytes(string: RocStr) callconv(.C) usize { pub fn strNumberOfBytes(string: RocStr) callconv(.C) usize {
return string.len(); return string.len();
} }
// Str.fromInt // Str.fromInt
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
pub fn strFromInt(int: i64) callconv(.C) RocStr { pub fn strFromIntC(int: i64) callconv(.C) RocStr {
// prepare for having multiple integer types in the future return strFromInt(std.heap.c_allocator, int);
return strFromIntHelp(i64, int);
} }
fn strFromIntHelp(comptime T: type, int: T) RocStr { inline fn strFromInt(allocator: *Allocator, int: i64) RocStr {
// prepare for having multiple integer types in the future
return strFromIntHelp(allocator, i64, int);
}
fn strFromIntHelp(allocator: *Allocator, comptime T: type, int: T) RocStr {
// determine maximum size for this T // determine maximum size for this T
comptime const size = comptime blk: { comptime const size = comptime blk: {
// the string representation of the minimum i128 value uses at most 40 characters // the string representation of the minimum i128 value uses at most 40 characters
@ -244,14 +253,18 @@ fn strFromIntHelp(comptime T: type, int: T) RocStr {
var buf: [size]u8 = undefined; var buf: [size]u8 = undefined;
const result = std.fmt.bufPrint(&buf, "{}", .{int}) catch unreachable; const result = std.fmt.bufPrint(&buf, "{}", .{int}) catch unreachable;
return RocStr.init(&buf, result.len); return RocStr.init(allocator, &buf, result.len);
} }
// Str.split // Str.split
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
pub fn strSplitInPlaceC(array: [*]RocStr, string: RocStr, delimiter: RocStr) callconv(.C) void {
strSplitInPlace(std.heap.c_allocator, array, string, delimiter);
}
pub fn strSplitInPlace(array: [*]RocStr, array_len: usize, string: RocStr, delimiter: RocStr) callconv(.C) void { inline fn strSplitInPlace(allocator: *Allocator, array: [*]RocStr, string: RocStr, delimiter: RocStr) void {
var ret_array_index: usize = 0; var ret_array_index: usize = 0;
var sliceStart_index: usize = 0; var slice_start_index: usize = 0;
var str_index: usize = 0; var str_index: usize = 0;
const str_bytes = string.asU8ptr(); const str_bytes = string.asU8ptr();
@ -279,10 +292,10 @@ pub fn strSplitInPlace(array: [*]RocStr, array_len: usize, string: RocStr, delim
} }
if (matches_delimiter) { if (matches_delimiter) {
const segment_len: usize = str_index - sliceStart_index; const segment_len: usize = str_index - slice_start_index;
array[ret_array_index] = RocStr.init(str_bytes + sliceStart_index, segment_len); array[ret_array_index] = RocStr.init(allocator, str_bytes + slice_start_index, segment_len);
sliceStart_index = str_index + delimiter_len; slice_start_index = str_index + delimiter_len;
ret_array_index += 1; ret_array_index += 1;
str_index += delimiter_len; str_index += delimiter_len;
} else { } else {
@ -291,44 +304,49 @@ pub fn strSplitInPlace(array: [*]RocStr, array_len: usize, string: RocStr, delim
} }
} }
array[ret_array_index] = RocStr.init(str_bytes + sliceStart_index, str_len - sliceStart_index); array[ret_array_index] = RocStr.init(allocator, str_bytes + slice_start_index, str_len - slice_start_index);
} }
test "strSplitInPlace: no delimiter" { test "strSplitInPlace: no delimiter" {
// Str.split "abc" "!" == [ "abc" ] // Str.split "abc" "!" == [ "abc" ]
const str_arr = "abc"; const str_arr = "abc";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "!"; const delimiter_arr = "!";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
var array: [1]RocStr = undefined; var array: [1]RocStr = undefined;
const array_ptr: [*]RocStr = &array; const array_ptr: [*]RocStr = &array;
strSplitInPlace(array_ptr, 1, str, delimiter); strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
var expected = [1]RocStr{ var expected = [1]RocStr{
str, str,
}; };
defer {
for (array) |roc_str| {
roc_str.deinit(testing.allocator);
}
for (expected) |roc_str| {
roc_str.deinit(testing.allocator);
}
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
expectEqual(array.len, expected.len); expectEqual(array.len, expected.len);
expect(array[0].eq(expected[0])); expect(array[0].eq(expected[0]));
for (array) |roc_str| {
roc_str.deinit();
}
for (expected) |roc_str| {
roc_str.deinit();
}
} }
test "strSplitInPlace: empty end" { test "strSplitInPlace: empty end" {
const str_arr = "1---- ---- ---- ---- ----2---- ---- ---- ---- ----"; const str_arr = "1---- ---- ---- ---- ----2---- ---- ---- ---- ----";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "---- ---- ---- ---- ----"; const delimiter_arr = "---- ---- ---- ---- ----";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
const array_len: usize = 3; const array_len: usize = 3;
var array: [array_len]RocStr = [_]RocStr{ var array: [array_len]RocStr = [_]RocStr{
@ -338,15 +356,28 @@ test "strSplitInPlace: empty end" {
}; };
const array_ptr: [*]RocStr = &array; const array_ptr: [*]RocStr = &array;
strSplitInPlace(array_ptr, array_len, str, delimiter); strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
const one = RocStr.init("1", 1); const one = RocStr.init(testing.allocator, "1", 1);
const two = RocStr.init("2", 1); const two = RocStr.init(testing.allocator, "2", 1);
var expected = [3]RocStr{ var expected = [3]RocStr{
one, two, RocStr.empty(), one, two, RocStr.empty(),
}; };
defer {
for (array) |rocStr| {
rocStr.deinit(testing.allocator);
}
for (expected) |rocStr| {
rocStr.deinit(testing.allocator);
}
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
expectEqual(array.len, expected.len); expectEqual(array.len, expected.len);
expect(array[0].eq(expected[0])); expect(array[0].eq(expected[0]));
expect(array[1].eq(expected[1])); expect(array[1].eq(expected[1]));
@ -355,10 +386,10 @@ test "strSplitInPlace: empty end" {
test "strSplitInPlace: delimiter on sides" { test "strSplitInPlace: delimiter on sides" {
const str_arr = "tttghittt"; const str_arr = "tttghittt";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "ttt"; const delimiter_arr = "ttt";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
const array_len: usize = 3; const array_len: usize = 3;
var array: [array_len]RocStr = [_]RocStr{ var array: [array_len]RocStr = [_]RocStr{
@ -367,15 +398,28 @@ test "strSplitInPlace: delimiter on sides" {
undefined, undefined,
}; };
const array_ptr: [*]RocStr = &array; const array_ptr: [*]RocStr = &array;
strSplitInPlace(array_ptr, array_len, str, delimiter); strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
const ghi_arr = "ghi"; const ghi_arr = "ghi";
const ghi = RocStr.init(ghi_arr, ghi_arr.len); const ghi = RocStr.init(testing.allocator, ghi_arr, ghi_arr.len);
var expected = [3]RocStr{ var expected = [3]RocStr{
RocStr.empty(), ghi, RocStr.empty(), RocStr.empty(), ghi, RocStr.empty(),
}; };
defer {
for (array) |rocStr| {
rocStr.deinit(testing.allocator);
}
for (expected) |rocStr| {
rocStr.deinit(testing.allocator);
}
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
expectEqual(array.len, expected.len); expectEqual(array.len, expected.len);
expect(array[0].eq(expected[0])); expect(array[0].eq(expected[0]));
expect(array[1].eq(expected[1])); expect(array[1].eq(expected[1]));
@ -385,25 +429,38 @@ test "strSplitInPlace: delimiter on sides" {
test "strSplitInPlace: three pieces" { test "strSplitInPlace: three pieces" {
// Str.split "a!b!c" "!" == [ "a", "b", "c" ] // Str.split "a!b!c" "!" == [ "a", "b", "c" ]
const str_arr = "a!b!c"; const str_arr = "a!b!c";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "!"; const delimiter_arr = "!";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
const array_len: usize = 3; const array_len: usize = 3;
var array: [array_len]RocStr = undefined; var array: [array_len]RocStr = undefined;
const array_ptr: [*]RocStr = &array; const array_ptr: [*]RocStr = &array;
strSplitInPlace(array_ptr, array_len, str, delimiter); strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
const a = RocStr.init("a", 1); const a = RocStr.init(testing.allocator, "a", 1);
const b = RocStr.init("b", 1); const b = RocStr.init(testing.allocator, "b", 1);
const c = RocStr.init("c", 1); const c = RocStr.init(testing.allocator, "c", 1);
var expected_array = [array_len]RocStr{ var expected_array = [array_len]RocStr{
a, b, c, a, b, c,
}; };
defer {
for (array) |roc_str| {
roc_str.deinit(testing.allocator);
}
for (expected_array) |roc_str| {
roc_str.deinit(testing.allocator);
}
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
expectEqual(expected_array.len, array.len); expectEqual(expected_array.len, array.len);
expect(array[0].eq(expected_array[0])); expect(array[0].eq(expected_array[0]));
expect(array[1].eq(expected_array[1])); expect(array[1].eq(expected_array[1]));
@ -459,13 +516,17 @@ test "countSegments: long delimiter" {
// Str.split "str" "delimiter" == [ "str" ] // Str.split "str" "delimiter" == [ "str" ]
// 1 segment // 1 segment
const str_arr = "str"; const str_arr = "str";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "delimiter"; const delimiter_arr = "delimiter";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
defer {
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
const segments_count = countSegments(str, delimiter); const segments_count = countSegments(str, delimiter);
expectEqual(segments_count, 1); expectEqual(segments_count, 1);
} }
@ -473,10 +534,15 @@ test "countSegments: delimiter at start" {
// Str.split "hello there" "hello" == [ "", " there" ] // Str.split "hello there" "hello" == [ "", " there" ]
// 2 segments // 2 segments
const str_arr = "hello there"; const str_arr = "hello there";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "hello"; const delimiter_arr = "hello";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
defer {
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
const segments_count = countSegments(str, delimiter); const segments_count = countSegments(str, delimiter);
@ -487,10 +553,15 @@ test "countSegments: delimiter interspered" {
// Str.split "a!b!c" "!" == [ "a", "b", "c" ] // Str.split "a!b!c" "!" == [ "a", "b", "c" ]
// 3 segments // 3 segments
const str_arr = "a!b!c"; const str_arr = "a!b!c";
const str = RocStr.init(str_arr, str_arr.len); const str = RocStr.init(testing.allocator, str_arr, str_arr.len);
const delimiter_arr = "!"; const delimiter_arr = "!";
const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
defer {
str.deinit(testing.allocator);
delimiter.deinit(testing.allocator);
}
const segments_count = countSegments(str, delimiter); const segments_count = countSegments(str, delimiter);
@ -499,7 +570,6 @@ test "countSegments: delimiter interspered" {
// Str.countGraphemeClusters // Str.countGraphemeClusters
const grapheme = @import("helpers/grapheme.zig"); const grapheme = @import("helpers/grapheme.zig");
pub fn countGraphemeClusters(string: RocStr) callconv(.C) usize { pub fn countGraphemeClusters(string: RocStr) callconv(.C) usize {
if (string.isEmpty()) { if (string.isEmpty()) {
return 0; return 0;
@ -545,40 +615,54 @@ test "countGraphemeClusters: empty string" {
test "countGraphemeClusters: ascii characters" { test "countGraphemeClusters: ascii characters" {
const bytes_arr = "abcd"; const bytes_arr = "abcd";
const bytes_len = bytes_arr.len; const bytes_len = bytes_arr.len;
const count = countGraphemeClusters(RocStr.init(bytes_arr, bytes_len)); const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
defer str.deinit(testing.allocator);
const count = countGraphemeClusters(str);
expectEqual(count, 4); expectEqual(count, 4);
} }
test "countGraphemeClusters: utf8 characters" { test "countGraphemeClusters: utf8 characters" {
const bytes_arr = "ãxā"; const bytes_arr = "ãxā";
const bytes_len = bytes_arr.len; const bytes_len = bytes_arr.len;
const count = countGraphemeClusters(RocStr.init(bytes_arr, bytes_len)); const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
defer str.deinit(testing.allocator);
const count = countGraphemeClusters(str);
expectEqual(count, 3); expectEqual(count, 3);
} }
test "countGraphemeClusters: emojis" { test "countGraphemeClusters: emojis" {
const bytes_arr = "🤔🤔🤔"; const bytes_arr = "🤔🤔🤔";
const bytes_len = bytes_arr.len; const bytes_len = bytes_arr.len;
const count = countGraphemeClusters(RocStr.init(bytes_arr, bytes_len)); const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
defer str.deinit(testing.allocator);
const count = countGraphemeClusters(str);
expectEqual(count, 3); expectEqual(count, 3);
} }
test "countGraphemeClusters: emojis and ut8 characters" { test "countGraphemeClusters: emojis and ut8 characters" {
const bytes_arr = "🤔å🤔¥🤔ç"; const bytes_arr = "🤔å🤔¥🤔ç";
const bytes_len = bytes_arr.len; const bytes_len = bytes_arr.len;
const count = countGraphemeClusters(RocStr.init(bytes_arr, bytes_len)); const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
defer str.deinit(testing.allocator);
const count = countGraphemeClusters(str);
expectEqual(count, 6); expectEqual(count, 6);
} }
test "countGraphemeClusters: emojis, ut8, and ascii characters" { test "countGraphemeClusters: emojis, ut8, and ascii characters" {
const bytes_arr = "6🤔å🤔e¥🤔çpp"; const bytes_arr = "6🤔å🤔e¥🤔çpp";
const bytes_len = bytes_arr.len; const bytes_len = bytes_arr.len;
const count = countGraphemeClusters(RocStr.init(bytes_arr, bytes_len)); const str = RocStr.init(testing.allocator, bytes_arr, bytes_len);
defer str.deinit(testing.allocator);
const count = countGraphemeClusters(str);
expectEqual(count, 10); expectEqual(count, 10);
} }
// Str.startsWith // Str.startsWith
pub fn startsWith(string: RocStr, prefix: RocStr) callconv(.C) bool { pub fn startsWith(string: RocStr, prefix: RocStr) callconv(.C) bool {
const bytes_len = string.len(); const bytes_len = string.len();
const bytes_ptr = string.asU8ptr(); const bytes_ptr = string.asU8ptr();
@ -602,25 +686,27 @@ pub fn startsWith(string: RocStr, prefix: RocStr) callconv(.C) bool {
} }
test "startsWith: foo starts with fo" { test "startsWith: foo starts with fo" {
const foo = RocStr.init("foo", 3); const foo = RocStr.init(testing.allocator, "foo", 3);
const fo = RocStr.init("fo", 2); const fo = RocStr.init(testing.allocator, "fo", 2);
expect(startsWith(foo, fo)); expect(startsWith(foo, fo));
} }
test "startsWith: 123456789123456789 starts with 123456789123456789" { test "startsWith: 123456789123456789 starts with 123456789123456789" {
const str = RocStr.init("123456789123456789", 18); const str = RocStr.init(testing.allocator, "123456789123456789", 18);
defer str.deinit(testing.allocator);
expect(startsWith(str, str)); expect(startsWith(str, str));
} }
test "startsWith: 12345678912345678910 starts with 123456789123456789" { test "startsWith: 12345678912345678910 starts with 123456789123456789" {
const str = RocStr.init("12345678912345678910", 20); const str = RocStr.init(testing.allocator, "12345678912345678910", 20);
const prefix = RocStr.init("123456789123456789", 18); defer str.deinit(testing.allocator);
const prefix = RocStr.init(testing.allocator, "123456789123456789", 18);
defer prefix.deinit(testing.allocator);
expect(startsWith(str, prefix)); expect(startsWith(str, prefix));
} }
// Str.endsWith // Str.endsWith
pub fn endsWith(string: RocStr, suffix: RocStr) callconv(.C) bool { pub fn endsWith(string: RocStr, suffix: RocStr) callconv(.C) bool {
const bytes_len = string.len(); const bytes_len = string.len();
const bytes_ptr = string.asU8ptr(); const bytes_ptr = string.asU8ptr();
@ -644,71 +730,57 @@ pub fn endsWith(string: RocStr, suffix: RocStr) callconv(.C) bool {
} }
test "endsWith: foo ends with oo" { test "endsWith: foo ends with oo" {
const foo = RocStr.init("foo", 3); const foo = RocStr.init(testing.allocator, "foo", 3);
const oo = RocStr.init("oo", 2); const oo = RocStr.init(testing.allocator, "oo", 2);
defer foo.deinit(testing.allocator);
defer oo.deinit(testing.allocator);
expect(endsWith(foo, oo)); expect(endsWith(foo, oo));
} }
test "endsWith: 123456789123456789 ends with 123456789123456789" { test "endsWith: 123456789123456789 ends with 123456789123456789" {
const str = RocStr.init("123456789123456789", 18); const str = RocStr.init(testing.allocator, "123456789123456789", 18);
defer str.deinit(testing.allocator);
expect(endsWith(str, str)); expect(endsWith(str, str));
} }
test "endsWith: 12345678912345678910 ends with 345678912345678910" { test "endsWith: 12345678912345678910 ends with 345678912345678910" {
const str = RocStr.init("12345678912345678910", 20); const str = RocStr.init(testing.allocator, "12345678912345678910", 20);
const suffix = RocStr.init("345678912345678910", 18); const suffix = RocStr.init(testing.allocator, "345678912345678910", 18);
defer str.deinit(testing.allocator);
defer suffix.deinit(testing.allocator);
expect(endsWith(str, suffix)); expect(endsWith(str, suffix));
} }
test "endsWith: hello world ends with world" { test "endsWith: hello world ends with world" {
const str = RocStr.init("hello world", 11); const str = RocStr.init(testing.allocator, "hello world", 11);
const suffix = RocStr.init("world", 5); const suffix = RocStr.init(testing.allocator, "world", 5);
defer str.deinit(testing.allocator);
defer suffix.deinit(testing.allocator);
expect(endsWith(str, suffix)); expect(endsWith(str, suffix));
} }
// Str.concat // Str.concat
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
test "RocStr.concat: small concat small" { pub fn strConcatC(ptr_size: u32, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) callconv(.C) RocStr {
const str1_len = 3; return strConcat(std.heap.c_allocator, ptr_size, result_in_place, arg1, arg2);
var str1: [str1_len]u8 = "foo".*;
const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(str1_ptr, str1_len);
const str2_len = 3;
var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(str2_ptr, str2_len);
const str3_len = 6;
var str3: [str3_len]u8 = "fooabc".*;
const str3_ptr: [*]u8 = &str3;
var roc_str3 = RocStr.init(str3_ptr, str3_len);
const result = strConcat(8, InPlace.Clone, roc_str1, roc_str2);
expect(roc_str3.eq(result));
roc_str1.deinit();
roc_str2.deinit();
roc_str3.deinit();
result.deinit();
} }
pub fn strConcat(ptr_size: u32, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) callconv(.C) RocStr { inline fn strConcat(allocator: *Allocator, ptr_size: u32, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr {
return switch (ptr_size) { return switch (ptr_size) {
4 => strConcatHelp(i32, result_in_place, arg1, arg2), 4 => strConcatHelp(allocator, i32, result_in_place, arg1, arg2),
8 => strConcatHelp(i64, result_in_place, arg1, arg2), 8 => strConcatHelp(allocator, i64, result_in_place, arg1, arg2),
else => unreachable, else => unreachable,
}; };
} }
fn strConcatHelp(comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr { fn strConcatHelp(allocator: *Allocator, comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr {
if (arg1.isEmpty()) { if (arg1.isEmpty()) {
return cloneStr(T, result_in_place, arg2); return cloneStr(allocator, T, result_in_place, arg2);
} else if (arg2.isEmpty()) { } else if (arg2.isEmpty()) {
return cloneStr(T, result_in_place, arg1); return cloneStr(allocator, T, result_in_place, arg1);
} else { } else {
const combined_length = arg1.len() + arg2.len(); const combined_length = arg1.len() + arg2.len();
@ -716,7 +788,7 @@ fn strConcatHelp(comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2:
const result_is_big = combined_length >= small_str_bytes; const result_is_big = combined_length >= small_str_bytes;
if (result_is_big) { if (result_is_big) {
var result = allocateStr(T, result_in_place, combined_length); var result = allocateStr(allocator, T, result_in_place, combined_length);
{ {
const old_if_small = &@bitCast([16]u8, arg1); const old_if_small = &@bitCast([16]u8, arg1);
@ -775,12 +847,12 @@ const InPlace = packed enum(u8) {
Clone, Clone,
}; };
fn cloneStr(comptime T: type, in_place: InPlace, str: RocStr) RocStr { fn cloneStr(allocator: *Allocator, comptime T: type, in_place: InPlace, str: RocStr) RocStr {
if (str.isSmallStr() or str.isEmpty()) { if (str.isSmallStr() or str.isEmpty()) {
// just return the bytes // just return the bytes
return str; return str;
} else { } else {
var new_str = allocateStr(T, in_place, str.str_len); var new_str = allocateStr(allocator, T, in_place, str.str_len);
var old_bytes: [*]u8 = @ptrCast([*]u8, str.str_bytes); var old_bytes: [*]u8 = @ptrCast([*]u8, str.str_bytes);
var new_bytes: [*]u8 = @ptrCast([*]u8, new_str.str_bytes); var new_bytes: [*]u8 = @ptrCast([*]u8, new_str.str_bytes);
@ -791,9 +863,9 @@ fn cloneStr(comptime T: type, in_place: InPlace, str: RocStr) RocStr {
} }
} }
fn allocateStr(comptime T: type, in_place: InPlace, number_of_chars: u64) RocStr { fn allocateStr(allocator: *Allocator, comptime T: type, in_place: InPlace, number_of_chars: u64) RocStr {
const length = @sizeOf(T) + number_of_chars; const length = @sizeOf(T) + number_of_chars;
var new_bytes: [*]T = @ptrCast([*]T, @alignCast(@alignOf(T), malloc(length))); var new_bytes: []T = allocator.alloc(T, length) catch unreachable;
if (in_place == InPlace.InPlace) { if (in_place == InPlace.InPlace) {
new_bytes[0] = @intCast(T, number_of_chars); new_bytes[0] = @intCast(T, number_of_chars);
@ -809,3 +881,32 @@ fn allocateStr(comptime T: type, in_place: InPlace, number_of_chars: u64) RocStr
.str_len = number_of_chars, .str_len = number_of_chars,
}; };
} }
test "RocStr.concat: small concat small" {
const str1_len = 3;
var str1: [str1_len]u8 = "foo".*;
const str1_ptr: [*]u8 = &str1;
var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len);
const str2_len = 3;
var str2: [str2_len]u8 = "abc".*;
const str2_ptr: [*]u8 = &str2;
var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len);
const str3_len = 6;
var str3: [str3_len]u8 = "fooabc".*;
const str3_ptr: [*]u8 = &str3;
var roc_str3 = RocStr.init(testing.allocator, str3_ptr, str3_len);
defer {
roc_str1.deinit(testing.allocator);
roc_str2.deinit(testing.allocator);
roc_str3.deinit(testing.allocator);
}
const result = strConcat(testing.allocator, 8, InPlace.Clone, roc_str1, roc_str2);
defer result.deinit(testing.allocator);
expect(roc_str3.eq(result));
}

View file

@ -1,6 +1,8 @@
use std::convert::AsRef; use std::convert::AsRef;
use std::env; use std::env;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs::{self};
use std::io;
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use std::str; use std::str;
@ -11,65 +13,104 @@ fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = env::var_os("OUT_DIR").unwrap();
// "." is relative to where "build.rs" is // "." is relative to where "build.rs" is
let src_path = Path::new(".").join("bitcode").join("src"); let build_script_dir_path = Path::new(".");
let main_zig_path = src_path.join("main.zig");
let src_path_str = src_path.to_str().expect("Invalid src path"); let bitcode_path = build_script_dir_path.join("bitcode");
println!("Building main.zig from: {}", src_path_str);
let zig_cache_dir = Path::new(&out_dir).join("zig-cache"); let src_path = bitcode_path.join("src");
let zig_cache_dir_str = zig_cache_dir.to_str().expect("Invalid zig cache dir");
println!("Setting zig cache to: {}", zig_cache_dir_str);
let dest_ll_path = Path::new(&out_dir).join("builtins.ll"); let build_zig_path = bitcode_path.join("build.zig");
let dest_ll = dest_ll_path.to_str().expect("Invalid dest ir path"); let build_zig = build_zig_path.to_str().expect("Invalid build path");
let emit_ir_arg = "-femit-llvm-ir=".to_owned() + dest_ll;
println!("Compiling zig llvm-ir to: {}", dest_ll); let dest_ir_path = build_script_dir_path.join("builtins.ll");
let dest_ir = dest_ir_path.to_str().expect("Invalid dest ir path");
println!("Compiling ir to: {}", dest_ir);
run_command( run_command(
"zig", "zig",
&[ &["build", "ir", "-Drelease=true", "--build-file", build_zig],
"build-obj",
main_zig_path.to_str().unwrap(),
&emit_ir_arg,
"-fno-emit-bin",
"--strip",
"-O",
"ReleaseFast",
"--cache-dir",
zig_cache_dir_str,
],
); );
let dest_bc_path = Path::new(&out_dir).join("builtins.bc"); let dest_bc_path = Path::new(&out_dir).join("builtins.bc");
let dest_bc = dest_bc_path.to_str().expect("Invalid dest bc path"); let dest_bc = dest_bc_path.to_str().expect("Invalid dest bc path");
println!("Compiling bitcode to: {}", dest_bc); println!("Compiling bitcode to: {}", dest_bc);
run_command("llvm-as-10", &[dest_ll, "-o", dest_bc]); run_command("llvm-as-10", &[dest_ir, "-o", dest_bc]);
// TODO: Recursivly search zig src dir to watch for each file
println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed={}", src_path_str);
println!("cargo:rustc-env=BUILTINS_BC={}", dest_bc); println!("cargo:rustc-env=BUILTINS_BC={}", dest_bc);
get_zig_files(src_path.as_path(), &|path| {
let path: &Path = path;
println!(
"cargo:rerun-if-changed={}",
path.to_str().expect("Failed to convert path to str")
);
})
.unwrap();
} }
fn run_command<S, I>(command: &str, args: I) fn run_command<S, I>(command_str: &str, args: I)
where where
I: IntoIterator<Item = S>, I: IntoIterator<Item = S>,
S: AsRef<OsStr>, S: AsRef<OsStr>,
{ {
let output_result = Command::new(OsStr::new(&command)).args(args).output(); let output_result = Command::new(OsStr::new(&command_str)).args(args).output();
match output_result { match output_result {
Ok(output) => match output.status.success() { Ok(output) => match output.status.success() {
true => (), true => (),
false => { false => {
let error_str = match str::from_utf8(&output.stderr) { let error_str = match str::from_utf8(&output.stderr) {
Ok(stderr) => stderr.to_string(), Ok(stderr) => stderr.to_string(),
Err(_) => format!("Failed to run \"{}\"", command), Err(_) => format!("Failed to run \"{}\"", command_str),
}; };
panic!("{} failed: {}", command, error_str); panic!("{} failed: {}", command_str, error_str);
} }
}, },
Err(reason) => panic!("{} failed: {}", command, reason), Err(reason) => panic!("{} failed: {}", command_str, reason),
} }
} }
fn get_zig_files(dir: &Path, cb: &dyn Fn(&Path)) -> io::Result<()> {
if dir.is_dir() {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path_buf = entry.path();
if path_buf.is_dir() {
get_zig_files(&path_buf, cb).unwrap();
} else {
let path = path_buf.as_path();
match path.extension() {
Some(osstr) if osstr == "zig" => {
cb(path);
}
_ => {}
}
}
}
}
Ok(())
}
// fn get_zig_files(dir: &Path) -> io::Result<Vec<&Path>> {
// let mut vec = Vec::new();
// if dir.is_dir() {
// for entry in fs::read_dir(dir)? {
// let entry = entry?;
// let path_buf = entry.path();
// if path_buf.is_dir() {
// match get_zig_files(&path_buf) {
// Ok(sub_files) => vec = [vec, sub_files].concat(),
// Err(_) => (),
// };
// } else {
// let path = path_buf.as_path();
// let path_ext = path.extension().unwrap();
// if path_ext == "zig" {
// vec.push(path.clone());
// }
// }
// }
// }
// Ok(vec)
// }

View file

@ -49,12 +49,7 @@ pub fn str_split<'a, 'ctx, 'env>(
call_void_bitcode_fn( call_void_bitcode_fn(
env, env,
&[ &[ret_list_ptr_zig_rocstr, str_i128.into(), delim_i128.into()],
ret_list_ptr_zig_rocstr,
BasicValueEnum::IntValue(segment_count),
str_i128.into(),
delim_i128.into(),
],
&bitcode::STR_STR_SPLIT_IN_PLACE, &bitcode::STR_STR_SPLIT_IN_PLACE,
); );

View file

@ -61,7 +61,11 @@ zerocopy = "0.3"
env_logger = "0.7" env_logger = "0.7"
futures = "0.3" futures = "0.3"
wgpu_glyph = "0.10" wgpu_glyph = "0.10"
bytemuck = "1.4" cgmath = "0.17.0"
[dependencies.bytemuck]
version = "1.4"
features = ["derive"]
[dev-dependencies] [dev-dependencies]
pretty_assertions = "0.5.1" pretty_assertions = "0.5.1"

101
editor/src/buffer.rs Normal file
View file

@ -0,0 +1,101 @@
// Adapted from https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
use crate::rect::Rect;
use crate::util::size_of_slice;
use crate::vertex::Vertex;
use wgpu::util::{BufferInitDescriptor, DeviceExt};
pub struct QuadBufferBuilder {
vertex_data: Vec<Vertex>,
index_data: Vec<u32>,
current_quad: u32,
}
impl QuadBufferBuilder {
pub fn new() -> Self {
Self {
vertex_data: Vec::new(),
index_data: Vec::new(),
current_quad: 0,
}
}
pub fn push_rect(self, rect: &Rect) -> Self {
let coords = rect.top_left_coords;
self.push_quad(
coords.x,
coords.y - rect.height,
coords.x + rect.width,
coords.y,
rect.color,
)
}
pub fn push_quad(
mut self,
min_x: f32,
min_y: f32,
max_x: f32,
max_y: f32,
color: [f32; 3],
) -> Self {
self.vertex_data.extend(&[
Vertex {
position: (min_x, min_y).into(),
color,
},
Vertex {
position: (max_x, min_y).into(),
color,
},
Vertex {
position: (max_x, max_y).into(),
color,
},
Vertex {
position: (min_x, max_y).into(),
color,
},
]);
self.index_data.extend(&[
self.current_quad * 4,
self.current_quad * 4 + 1,
self.current_quad * 4 + 2,
self.current_quad * 4,
self.current_quad * 4 + 2,
self.current_quad * 4 + 3,
]);
self.current_quad += 1;
self
}
pub fn build(self, device: &wgpu::Device) -> (StagingBuffer, StagingBuffer, u32) {
(
StagingBuffer::new(device, &self.vertex_data),
StagingBuffer::new(device, &self.index_data),
self.index_data.len() as u32,
)
}
}
pub struct StagingBuffer {
buffer: wgpu::Buffer,
size: wgpu::BufferAddress,
}
impl StagingBuffer {
pub fn new<T: bytemuck::Pod + Sized>(device: &wgpu::Device, data: &[T]) -> StagingBuffer {
StagingBuffer {
buffer: device.create_buffer_init(&BufferInitDescriptor {
contents: bytemuck::cast_slice(data),
usage: wgpu::BufferUsage::COPY_SRC,
label: Some("Staging Buffer"),
}),
size: size_of_slice(data) as wgpu::BufferAddress,
}
}
pub fn copy_to_buffer(&self, encoder: &mut wgpu::CommandEncoder, other: &wgpu::Buffer) {
encoder.copy_buffer_to_buffer(&self.buffer, 0, other, 0, self.size)
}
}

View file

@ -11,21 +11,29 @@
// re-enable this when working on performance optimizations than have it block PRs. // re-enable this when working on performance optimizations than have it block PRs.
#![allow(clippy::large_enum_variant)] #![allow(clippy::large_enum_variant)]
// Inspired by https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
// See this link to learn wgpu: https://sotrh.github.io/learn-wgpu/
use crate::buffer::QuadBufferBuilder;
use crate::rect::Rect; use crate::rect::Rect;
use crate::vertex::Vertex; use crate::vertex::Vertex;
use std::error::Error; use std::error::Error;
use std::io; use std::io;
use std::path::Path; use std::path::Path;
use wgpu::util::DeviceExt;
use wgpu_glyph::{ab_glyph, GlyphBrushBuilder, Section, Text}; use wgpu_glyph::{ab_glyph, GlyphBrushBuilder, Section, Text};
use winit::event::{ElementState, ModifiersState, VirtualKeyCode}; use winit::event;
use winit::event::{ElementState, Event, ModifiersState, VirtualKeyCode};
use winit::event_loop::ControlFlow; use winit::event_loop::ControlFlow;
pub mod ast; pub mod ast;
pub mod bucket; pub mod bucket;
mod buffer;
pub mod file; pub mod file;
mod rect; mod rect;
pub mod text_state; pub mod text_state;
mod util;
mod vertex; mod vertex;
/// The editor is actually launched from the CLI if you pass it zero arguments, /// The editor is actually launched from the CLI if you pass it zero arguments,
@ -53,7 +61,7 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
let surface = unsafe { instance.create_surface(&window) }; let surface = unsafe { instance.create_surface(&window) };
// Initialize GPU // Initialize GPU
let (device, queue) = futures::executor::block_on(async { let (gpu_device, cmd_queue) = futures::executor::block_on(async {
let adapter = instance let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance, power_preference: wgpu::PowerPreference::HighPerformance,
@ -83,59 +91,26 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
// Prepare swap chain // Prepare swap chain
let render_format = wgpu::TextureFormat::Bgra8UnormSrgb; let render_format = wgpu::TextureFormat::Bgra8UnormSrgb;
let mut size = window.inner_size(); let mut size = window.inner_size();
let mut swap_chain = device.create_swap_chain(
&surface,
&wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: render_format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Immediate,
},
);
// Prepare Triangle Pipeline let swap_chain_descr = wgpu::SwapChainDescriptor {
let triangle_vs_module = usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
device.create_shader_module(wgpu::include_spirv!("shaders/rect.vert.spv")); format: render_format,
let triangle_fs_module = width: size.width,
device.create_shader_module(wgpu::include_spirv!("shaders/rect.frag.spv")); height: size.height,
//Immediate may cause tearing, change present_mode if this becomes a problem
present_mode: wgpu::PresentMode::Immediate,
};
let triangle_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { let mut swap_chain = gpu_device.create_swap_chain(&surface, &swap_chain_descr);
label: None,
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let triangle_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { let rect_pipeline = make_rect_pipeline(&gpu_device, &swap_chain_descr);
label: None,
layout: Some(&triangle_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &triangle_vs_module,
entry_point: "main",
},
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
module: &triangle_fs_module,
entry_point: "main",
}),
// Use the default rasterizer state: no culling, no depth bias
rasterization_state: None,
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::TextureFormat::Bgra8UnormSrgb.into()],
depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[Vertex::buffer_descriptor()],
},
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
});
// Prepare glyph_brush // Prepare glyph_brush
let inconsolata = let inconsolata =
ab_glyph::FontArc::try_from_slice(include_bytes!("../Inconsolata-Regular.ttf"))?; ab_glyph::FontArc::try_from_slice(include_bytes!("../Inconsolata-Regular.ttf"))?;
let mut glyph_brush = GlyphBrushBuilder::using_font(inconsolata).build(&device, render_format); let mut glyph_brush =
GlyphBrushBuilder::using_font(inconsolata).build(&gpu_device, render_format);
let is_animating = true; let is_animating = true;
let mut text_state = String::new(); let mut text_state = String::new();
@ -154,178 +129,93 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
} }
match event { match event {
winit::event::Event::WindowEvent { Event::WindowEvent {
event: winit::event::WindowEvent::CloseRequested, event: event::WindowEvent::CloseRequested,
.. ..
} => *control_flow = winit::event_loop::ControlFlow::Exit, } => *control_flow = winit::event_loop::ControlFlow::Exit,
winit::event::Event::WindowEvent { Event::WindowEvent {
event: winit::event::WindowEvent::Resized(new_size), event: event::WindowEvent::Resized(new_size),
.. ..
} => { } => {
size = new_size; size = new_size;
swap_chain = device.create_swap_chain( swap_chain = gpu_device.create_swap_chain(
&surface, &surface,
&wgpu::SwapChainDescriptor { &wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: render_format, format: render_format,
width: size.width, width: size.width,
height: size.height, height: size.height,
//Immediate may cause tearing, change present_mode if this becomes a problem
present_mode: wgpu::PresentMode::Immediate, present_mode: wgpu::PresentMode::Immediate,
}, },
); );
} }
winit::event::Event::WindowEvent { Event::WindowEvent {
event: winit::event::WindowEvent::ReceivedCharacter(ch), event: event::WindowEvent::ReceivedCharacter(ch),
.. ..
} => { } => {
match ch { update_text_state(&mut text_state, &ch);
'\u{8}' | '\u{7f}' => {
// In Linux, we get a '\u{8}' when you press backspace,
// but in macOS we get '\u{7f}'.
text_state.pop();
}
'\u{e000}'..='\u{f8ff}'
| '\u{f0000}'..='\u{ffffd}'
| '\u{100000}'..='\u{10fffd}' => {
// These are private use characters; ignore them.
// See http://www.unicode.org/faq/private_use.html
}
_ => {
text_state.push(ch);
}
}
} }
winit::event::Event::WindowEvent { Event::WindowEvent {
event: winit::event::WindowEvent::KeyboardInput { input, .. }, event: event::WindowEvent::KeyboardInput { input, .. },
.. ..
} => { } => {
if let Some(virtual_keycode) = input.virtual_keycode { if let Some(virtual_keycode) = input.virtual_keycode {
handle_keydown(input.state, virtual_keycode, keyboard_modifiers); handle_keydown(input.state, virtual_keycode, keyboard_modifiers);
} }
} }
winit::event::Event::WindowEvent { Event::WindowEvent {
event: winit::event::WindowEvent::ModifiersChanged(modifiers), event: event::WindowEvent::ModifiersChanged(modifiers),
.. ..
} => { } => {
keyboard_modifiers = modifiers; keyboard_modifiers = modifiers;
} }
winit::event::Event::MainEventsCleared => window.request_redraw(), Event::MainEventsCleared => window.request_redraw(),
winit::event::Event::RedrawRequested { .. } => { Event::RedrawRequested { .. } => {
// Get a command encoder for the current frame // Get a command encoder for the current frame
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder =
label: Some("Redraw"), gpu_device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
}); label: Some("Redraw"),
// Get the next frame
let frame = swap_chain
.get_current_frame()
.expect("Failed to acquire next swap chain texture")
.output;
// Test Rectangle
let test_rect_1 = Rect {
top: 0.9,
left: -0.8,
width: 0.2,
height: 0.3,
color: [0.0, 1.0, 1.0],
};
let test_rect_2 = Rect {
top: 0.0,
left: 0.0,
width: 0.5,
height: 0.5,
color: [1.0, 1.0, 0.0],
};
let mut rectangles = Vec::new();
rectangles.extend_from_slice(&test_rect_1.as_array());
rectangles.extend_from_slice(&test_rect_2.as_array());
let mut rect_index_buffers = Vec::new();
rect_index_buffers.extend_from_slice(&Rect::INDEX_BUFFER);
rect_index_buffers.extend_from_slice(&Rect::INDEX_BUFFER);
// Vertex Buffer for drawing rectangles
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(&rectangles),
usage: wgpu::BufferUsage::VERTEX,
});
// Index Buffer for drawing rectangles
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(&rect_index_buffers),
usage: wgpu::BufferUsage::INDEX,
});
// Clear frame
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.007,
g: 0.007,
b: 0.007,
a: 1.0,
}),
store: true,
},
}],
depth_stencil_attachment: None,
}); });
render_pass.set_pipeline(&triangle_pipeline); let rect_buffers = create_rect_buffers(&gpu_device, &mut encoder);
render_pass.set_vertex_buffer( let frame = swap_chain
0, // The buffer slot to use for this vertex buffer. .get_current_frame()
vertex_buffer.slice(..), // Use the entire buffer. .expect("Failed to acquire next SwapChainFrame")
); .output;
render_pass.set_index_buffer(index_buffer.slice(..)); let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view,
resolve_target: None,
ops: wgpu::Operations::default(),
}],
depth_stencil_attachment: None,
});
render_pass.draw_indexed( if rect_buffers.num_rects > 0 {
0..((&rect_index_buffers).len() as u32), // Draw all of the vertices from our test data. render_pass.set_vertex_buffer(0, rect_buffers.vertex_buffer.slice(..));
0, // Base Vertex render_pass.set_index_buffer(rect_buffers.index_buffer.slice(..));
0..1, // Instances render_pass.set_pipeline(&rect_pipeline);
); render_pass.draw_indexed(0..rect_buffers.num_rects, 0, 0..1);
} }
glyph_brush.queue(Section { drop(render_pass);
screen_position: (30.0, 30.0),
bounds: (size.width as f32, size.height as f32),
text: vec![Text::new("Enter some text:")
.with_color([0.4666, 0.2, 1.0, 1.0])
.with_scale(40.0)],
..Section::default()
});
glyph_brush.queue(Section { draw_text(
screen_position: (30.0, 90.0), &gpu_device,
bounds: (size.width as f32, size.height as f32), &mut staging_belt,
text: vec![Text::new(format!("{}|", text_state).as_str()) &mut encoder,
.with_color([1.0, 1.0, 1.0, 1.0]) &frame,
.with_scale(40.0)], &size,
..Section::default() &text_state,
}); &mut glyph_brush,
);
// Draw the text!
glyph_brush
.draw_queued(
&device,
&mut staging_belt,
&mut encoder,
&frame.view,
size.width,
size.height,
)
.expect("Draw queued");
staging_belt.finish(); staging_belt.finish();
queue.submit(Some(encoder.finish())); cmd_queue.submit(Some(encoder.finish()));
// Recall unused staging buffers // Recall unused staging buffers
use futures::task::SpawnExt; use futures::task::SpawnExt;
@ -343,6 +233,190 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
}) })
} }
fn make_rect_pipeline(
gpu_device: &wgpu::Device,
swap_chain_descr: &wgpu::SwapChainDescriptor,
) -> wgpu::RenderPipeline {
let pipeline_layout = gpu_device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[],
push_constant_ranges: &[],
label: Some("Pipeline Layout"),
});
let pipeline = create_render_pipeline(
&gpu_device,
&pipeline_layout,
swap_chain_descr.format,
&[Vertex::DESC],
wgpu::include_spirv!("shaders/rect.vert.spv"),
wgpu::include_spirv!("shaders/rect.frag.spv"),
);
pipeline
}
fn create_render_pipeline(
device: &wgpu::Device,
layout: &wgpu::PipelineLayout,
color_format: wgpu::TextureFormat,
vertex_descs: &[wgpu::VertexBufferDescriptor],
vs_src: wgpu::ShaderModuleSource,
fs_src: wgpu::ShaderModuleSource,
) -> wgpu::RenderPipeline {
let vs_module = device.create_shader_module(vs_src);
let fs_module = device.create_shader_module(fs_src);
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&layout),
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
},
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
module: &fs_module,
entry_point: "main",
}),
rasterization_state: None,
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
format: color_format,
color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL,
}],
depth_stencil_state: None,
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint32,
vertex_buffers: vertex_descs,
},
})
}
struct RectBuffers {
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
num_rects: u32,
}
fn create_rect_buffers(
gpu_device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
) -> RectBuffers {
// Test Rectangles
let test_rect_1 = Rect {
top_left_coords: (-0.2, 0.6).into(),
width: 0.1,
height: 0.5,
color: [0.0, 0.0, 1.0],
};
let test_rect_2 = Rect {
top_left_coords: (-0.5, 0.0).into(),
width: 0.5,
height: 0.5,
color: [0.0, 1.0, 0.0],
};
let test_rect_3 = Rect {
top_left_coords: (0.3, 0.3).into(),
width: 0.6,
height: 0.1,
color: [0.0, 0.0, 1.0],
};
let vertex_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: Vertex::SIZE * 4 * 3,
usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
mapped_at_creation: false,
});
let u32_size = std::mem::size_of::<u32>() as wgpu::BufferAddress;
let index_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: u32_size * 6 * 3,
usage: wgpu::BufferUsage::INDEX | wgpu::BufferUsage::COPY_DST,
mapped_at_creation: false,
});
let num_rects = {
let (stg_vertex, stg_index, num_indices) = QuadBufferBuilder::new()
.push_rect(&test_rect_1)
.push_rect(&test_rect_2)
.push_rect(&test_rect_3)
.build(&gpu_device);
stg_vertex.copy_to_buffer(encoder, &vertex_buffer);
stg_index.copy_to_buffer(encoder, &index_buffer);
num_indices
};
RectBuffers {
vertex_buffer,
index_buffer,
num_rects,
}
}
fn draw_text(
gpu_device: &wgpu::Device,
staging_belt: &mut wgpu::util::StagingBelt,
encoder: &mut wgpu::CommandEncoder,
frame: &wgpu::SwapChainTexture,
size: &winit::dpi::PhysicalSize<u32>,
text_state: &str,
glyph_brush: &mut wgpu_glyph::GlyphBrush<()>,
) {
glyph_brush.queue(Section {
screen_position: (30.0, 30.0),
bounds: (size.width as f32, size.height as f32),
text: vec![Text::new("Enter some text:")
.with_color([0.4666, 0.2, 1.0, 1.0])
.with_scale(40.0)],
..Section::default()
});
glyph_brush.queue(Section {
screen_position: (30.0, 90.0),
bounds: (size.width as f32, size.height as f32),
text: vec![Text::new(format!("{}|", text_state).as_str())
.with_color([1.0, 1.0, 1.0, 1.0])
.with_scale(40.0)],
..Section::default()
});
// Draw the text!
glyph_brush
.draw_queued(
gpu_device,
staging_belt,
encoder,
&frame.view,
size.width,
size.height,
)
.expect("Draw queued");
}
fn update_text_state(text_state: &mut String, received_char: &char) {
match received_char {
'\u{8}' | '\u{7f}' => {
// In Linux, we get a '\u{8}' when you press backspace,
// but in macOS we get '\u{7f}'.
text_state.pop();
}
'\u{e000}'..='\u{f8ff}' | '\u{f0000}'..='\u{ffffd}' | '\u{100000}'..='\u{10fffd}' => {
// These are private use characters; ignore them.
// See http://www.unicode.org/faq/private_use.html
}
_ => {
text_state.push(*received_char);
}
}
}
fn handle_keydown( fn handle_keydown(
elem_state: ElementState, elem_state: ElementState,
virtual_keycode: VirtualKeyCode, virtual_keycode: VirtualKeyCode,

View file

@ -1,35 +1,8 @@
use crate::vertex::Vertex; use cgmath::Vector2;
pub struct Rect { pub struct Rect {
pub top: f32, pub top_left_coords: Vector2<f32>,
pub left: f32,
pub width: f32, pub width: f32,
pub height: f32, pub height: f32,
pub color: [f32; 3], pub color: [f32; 3],
} }
impl Rect {
pub fn as_array(&self) -> [Vertex; 4] {
[
Vertex {
position: [self.left, self.top, 0.0],
color: self.color,
},
Vertex {
position: [self.left + self.width, self.top, 0.0],
color: self.color,
},
Vertex {
position: [self.left + self.width, self.top - self.height, 0.0],
color: self.color,
},
Vertex {
position: [self.left, self.top - self.height, 0.0],
color: self.color,
},
]
}
// Currently broken - needs to be offset when additional rectangles are appended
pub const INDEX_BUFFER: [u16; 6] = [0, 1, 3, 1, 2, 3];
}

View file

@ -1,11 +1,11 @@
#version 450 #version 450
// Taken from https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
// The fragment shader's "in" values come from the "out" values of the vertex shader.
layout(location=0) in vec3 color; layout(location=0) in vec3 color;
// The actual color that is rendered to the screen based on the vertex. layout(location=0) out vec4 fColor;
layout(location=0) out vec4 f_color;
void main() { void main() {
f_color = vec4(color, 1.0); fColor = vec4(color, 1.0);
} }

View file

@ -1,14 +1,13 @@
#version 450 #version 450
// Taken from https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
// Layout value labelled "in" acquire data from the vertex buffer, layout(location=0) in vec2 aPosition;
// as defined in the buffer descriptor for this shader. layout(location=1) in vec3 aColor;
layout(location=0) in vec3 position;
layout(location=1) in vec3 color;
// Layout values labelled "out" send their data to the fragment shader. layout(location=0) out vec3 vColor;
layout(location=0) out vec3 v_color;
void main() { void main() {
v_color = color; gl_Position = vec4(aPosition, 0, 1);
gl_Position = vec4(position, 1.0); vColor = aColor;
} }

5
editor/src/util.rs Normal file
View file

@ -0,0 +1,5 @@
// Taken from https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
pub fn size_of_slice<T: Sized>(slice: &[T]) -> usize {
std::mem::size_of::<T>() * slice.len()
}

View file

@ -1,7 +1,11 @@
#[repr(C)] // Taken from https://github.com/sotrh/learn-wgpu
#[derive(Copy, Clone, Debug)] // by Benjamin Hansen, licensed under the MIT license
use cgmath::Vector2;
#[derive(Copy, Clone)]
pub struct Vertex { pub struct Vertex {
pub position: [f32; 3], #[allow(dead_code)]
pub position: Vector2<f32>,
pub color: [f32; 3], pub color: [f32; 3],
} }
@ -9,25 +13,23 @@ unsafe impl bytemuck::Pod for Vertex {}
unsafe impl bytemuck::Zeroable for Vertex {} unsafe impl bytemuck::Zeroable for Vertex {}
impl Vertex { impl Vertex {
// Defines how the shader will use this data structure. pub const SIZE: wgpu::BufferAddress = std::mem::size_of::<Self>() as wgpu::BufferAddress;
pub fn buffer_descriptor<'a>() -> wgpu::VertexBufferDescriptor<'a> { pub const DESC: wgpu::VertexBufferDescriptor<'static> = wgpu::VertexBufferDescriptor {
wgpu::VertexBufferDescriptor { stride: Self::SIZE,
stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Vertex,
step_mode: wgpu::InputStepMode::Vertex, attributes: &[
attributes: &[ // position
// position wgpu::VertexAttributeDescriptor {
wgpu::VertexAttributeDescriptor { offset: 0,
offset: 0, shader_location: 0,
shader_location: 0, format: wgpu::VertexFormat::Float2,
format: wgpu::VertexFormat::Float3, },
}, // color
// color wgpu::VertexAttributeDescriptor {
wgpu::VertexAttributeDescriptor { offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, shader_location: 1,
shader_location: 1, format: wgpu::VertexFormat::Float3,
format: wgpu::VertexFormat::Float3, },
}, ],
], };
}
}
} }

1
examples/balance/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
main

View file

@ -1,7 +1,7 @@
app Main provides [ rocMain ] imports [ Effect ] app "main" imports [ Effect ] provides [ rocMain ] to "./platform"
rocMain : Effect.Effect {} as Fx rocMain : Effect.Effect {} as Fx
rocMain = rocMain =
when List.len (Str.split "hello" "JJJJ there") is when List.len (Str.split "hello" "JJJJ there") is
_ -> Effect.putLine "Yay" _ -> Effect.putLine "Yay"

View file

@ -1,7 +1,9 @@
platform folkertdev/foo platform folkertdev/foo
provides [ mainForHost ] requires { rocMain : Effect {} }
requires { main : Effect {} } exposes []
packages {}
imports [] imports []
provides [ mainForHost ]
effects Effect effects Effect
{ {
putChar : Int -> Effect {}, putChar : Int -> Effect {},
@ -10,4 +12,4 @@ platform folkertdev/foo
} }
mainForHost : Effect {} as Fx mainForHost : Effect {} as Fx
mainForHost = main mainForHost = rocMain

View file

@ -7,16 +7,16 @@ use std::alloc::Layout;
use std::time::SystemTime; use std::time::SystemTime;
extern "C" { extern "C" {
#[link_name = "Main_rocMain_1_exposed"] #[link_name = "roc__rocMain_1_exposed"]
fn roc_main(output: *mut u8) -> (); fn roc_main(output: *mut u8) -> ();
#[link_name = "Main_rocMain_1_size"] #[link_name = "roc__rocMain_1_size"]
fn roc_main_size() -> i64; fn roc_main_size() -> i64;
#[link_name = "Main_rocMain_1_Fx_caller"] #[link_name = "roc__rocMain_1_Fx_caller"]
fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> (); fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
#[link_name = "Main_rocMain_1_Fx_size"] #[link_name = "roc__rocMain_1_Fx_size"]
fn size_Fx() -> i64; fn size_Fx() -> i64;
} }
@ -62,11 +62,10 @@ unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const
let output = &*(buffer as *mut RocCallResult<i64>); let output = &*(buffer as *mut RocCallResult<i64>);
// match output.into() { match output.into() {
// Ok(v) => v, Ok(_) => 0,
// Err(e) => panic!("failed with {}", e), Err(e) => panic!("failed with {}", e),
// } }
32
}) })
} }
@ -96,7 +95,12 @@ pub fn rust_main() -> isize {
let closure_data_ptr = buffer.offset(16); let closure_data_ptr = buffer.offset(16);
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8) let result =
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8);
std::alloc::dealloc(buffer, layout);
result
} }
Err(msg) => { Err(msg) => {
std::alloc::dealloc(buffer, layout); std::alloc::dealloc(buffer, layout);

1
examples/closure/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
closure

View file

@ -1,19 +1,21 @@
#![allow(non_snake_case)]
use roc_std::alloca; use roc_std::alloca;
use roc_std::RocCallResult; use roc_std::RocCallResult;
use std::alloc::Layout; use std::alloc::Layout;
use std::time::SystemTime; use std::time::SystemTime;
extern "C" { extern "C" {
#[link_name = "Main_makeClosure_1_exposed"] #[link_name = "roc__makeClosure_1_exposed"]
fn make_closure(output: *mut u8) -> (); fn make_closure(output: *mut u8) -> ();
#[link_name = "Main_makeClosure_1_size"] #[link_name = "roc__makeClosure_1_size"]
fn closure_size() -> i64; fn closure_size() -> i64;
#[link_name = "Main_makeClosure_1_MyClosure_caller"] #[link_name = "roc__makeClosure_1_MyClosure_caller"]
fn call_MyClosure(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> (); fn call_MyClosure(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
#[link_name = "Main_makeClosure_1_MyClosure_size"] #[link_name = "roc__makeClosure_1_MyClosure_size"]
fn size_MyClosure() -> i64; fn size_MyClosure() -> i64;
} }
@ -65,7 +67,12 @@ pub fn rust_main() -> isize {
let closure_data_ptr = buffer.offset(16); let closure_data_ptr = buffer.offset(16);
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8) let result =
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8);
std::alloc::dealloc(buffer, layout);
result
} }
Err(msg) => { Err(msg) => {
std::alloc::dealloc(buffer, layout); std::alloc::dealloc(buffer, layout);
@ -80,7 +87,6 @@ pub fn rust_main() -> isize {
println!( println!(
"Roc closure took {:.4} ms to compute this answer: {:?}", "Roc closure took {:.4} ms to compute this answer: {:?}",
duration.as_secs_f64() * 1000.0, duration.as_secs_f64() * 1000.0,
// truncate the answer, so stdout is not swamped
answer answer
); );

1
examples/effect/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
effect-example

1
examples/shared-quicksort/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
quicksort

View file

@ -1,9 +1,11 @@
#![allow(non_snake_case)]
use roc_std::RocCallResult; use roc_std::RocCallResult;
use roc_std::RocList; use roc_std::RocList;
use std::time::SystemTime; use std::time::SystemTime;
extern "C" { extern "C" {
#[link_name = "_quicksort_1_exposed"] #[link_name = "roc__quicksort_1_exposed"]
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> (); fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
} }

View file

@ -39,6 +39,7 @@ let
linux-inputs = linux-inputs =
if isLinux then if isLinux then
[ [
valgrind
vulkan-headers vulkan-headers
vulkan-loader vulkan-loader
vulkan-tools vulkan-tools
@ -71,7 +72,6 @@ let
python3 python3
llvmPkgs.llvm llvmPkgs.llvm
llvmPkgs.clang llvmPkgs.clang
valgrind
pkg-config pkg-config
zig zig
# llb deps # llb deps