mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
Merge remote-tracking branch 'origin/trunk' into editor-ir
This commit is contained in:
commit
1fe0f5d780
26 changed files with 785 additions and 460 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
@ -270,6 +270,20 @@ name = "bytemuck"
|
|||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "byteorder"
|
||||
|
@ -324,6 +338,17 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
|
@ -2637,6 +2662,7 @@ dependencies = [
|
|||
"arraystring",
|
||||
"bumpalo",
|
||||
"bytemuck",
|
||||
"cgmath",
|
||||
"env_logger 0.7.1",
|
||||
"fs_extra",
|
||||
"futures",
|
||||
|
|
1
compiler/builtins/.gitignore
vendored
Normal file
1
compiler/builtins/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
builtins.ll
|
|
@ -9,16 +9,13 @@ pub fn build(b: *Builder) void {
|
|||
|
||||
// Options
|
||||
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 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
|
||||
var main_tests = b.addTest(main_path);
|
||||
main_tests.setBuildMode(mode);
|
||||
main_tests.linkSystemLibrary("c");
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&main_tests.step);
|
||||
|
||||
|
@ -26,25 +23,13 @@ pub fn build(b: *Builder) void {
|
|||
const obj_name = "builtins";
|
||||
const obj = b.addObject(obj_name, main_path);
|
||||
obj.setBuildMode(mode);
|
||||
obj.linkSystemLibrary("c");
|
||||
obj.strip = true;
|
||||
obj.emit_llvm_ir = true;
|
||||
obj.emit_bin = false;
|
||||
const ir = b.step("ir", "Build LLVM ir");
|
||||
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;
|
||||
removeInstallSteps(b);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
set -euxo pipefail
|
||||
|
||||
# Test every zig
|
||||
find src/*.zig -type f -print0 | xargs -n 1 -0 zig test --library c
|
||||
zig build test
|
||||
|
||||
# fmt every zig
|
||||
find src/*.zig -type f -print0 | xargs -n 1 -0 zig fmt --check
|
||||
|
|
|
@ -15,14 +15,14 @@ comptime {
|
|||
// Str Module
|
||||
const str = @import("str.zig");
|
||||
comptime {
|
||||
exportStrFn(str.strSplitInPlace, "str_split_in_place");
|
||||
exportStrFn(str.strSplitInPlaceC, "str_split_in_place");
|
||||
exportStrFn(str.countSegments, "count_segments");
|
||||
exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters");
|
||||
exportStrFn(str.startsWith, "starts_with");
|
||||
exportStrFn(str.endsWith, "ends_with");
|
||||
exportStrFn(str.strConcat, "concat");
|
||||
exportStrFn(str.strConcatC, "concat");
|
||||
exportStrFn(str.strNumberOfBytes, "number_of_bytes");
|
||||
exportStrFn(str.strFromInt, "from_int");
|
||||
exportStrFn(str.strFromIntC, "from_int");
|
||||
}
|
||||
|
||||
// Export helpers - Must be run inside a comptime
|
||||
|
|
|
@ -1,26 +1,25 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const unicode = std.unicode;
|
||||
const testing = std.testing;
|
||||
const expectEqual = testing.expectEqual;
|
||||
const expect = testing.expect;
|
||||
|
||||
extern fn malloc(size: usize) ?*u8;
|
||||
extern fn free([*]u8) void;
|
||||
|
||||
const RocStr = extern struct {
|
||||
str_bytes: ?[*]u8,
|
||||
str_len: usize,
|
||||
|
||||
pub fn empty() RocStr {
|
||||
pub inline fn empty() RocStr {
|
||||
return RocStr{
|
||||
.str_len = 0,
|
||||
.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.
|
||||
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);
|
||||
|
||||
if (length < roc_str_size) {
|
||||
|
@ -40,7 +39,7 @@ const RocStr = extern struct {
|
|||
index = 0;
|
||||
while (index < length) {
|
||||
var offset_ptr = @intToPtr(*u8, target_ptr + index);
|
||||
offset_ptr.* = bytes[index];
|
||||
offset_ptr.* = bytes_ptr[index];
|
||||
index += 1;
|
||||
}
|
||||
|
||||
|
@ -50,9 +49,9 @@ const RocStr = extern struct {
|
|||
|
||||
return ret_small_str;
|
||||
} 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;
|
||||
}
|
||||
|
@ -66,20 +65,23 @@ const RocStr = extern struct {
|
|||
if (length < roc_str_size) {
|
||||
return RocStr.empty();
|
||||
} 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{
|
||||
.str_bytes = new_bytes,
|
||||
.str_bytes = new_bytes_ptr,
|
||||
.str_len = length,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: RocStr) void {
|
||||
if (!self.isSmallStr()) {
|
||||
const str_bytes: [*]u8 = self.str_bytes orelse unreachable;
|
||||
pub fn deinit(self: RocStr, allocator: *Allocator) void {
|
||||
if (!self.isSmallStr() and !self.isEmpty()) {
|
||||
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;
|
||||
var str1: [str1_len]u8 = "abc".*;
|
||||
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;
|
||||
var str2: [str2_len]u8 = "abc".*;
|
||||
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
|
||||
// expect(roc_str1.eq(roc_str2));
|
||||
|
||||
roc_str1.deinit();
|
||||
roc_str2.deinit();
|
||||
roc_str1.deinit(testing.allocator);
|
||||
roc_str2.deinit(testing.allocator);
|
||||
}
|
||||
|
||||
test "RocStr.eq: not equal different length" {
|
||||
const str1_len = 4;
|
||||
var str1: [str1_len]u8 = "abcd".*;
|
||||
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;
|
||||
var str2: [str2_len]u8 = "abc".*;
|
||||
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));
|
||||
|
||||
roc_str1.deinit();
|
||||
roc_str2.deinit();
|
||||
}
|
||||
|
||||
test "RocStr.eq: not equal same length" {
|
||||
const str1_len = 3;
|
||||
var str1: [str1_len]u8 = "acb".*;
|
||||
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;
|
||||
var str2: [str2_len]u8 = "abc".*;
|
||||
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
|
||||
// expect(!roc_str1.eq(roc_str2));
|
||||
|
||||
roc_str1.deinit();
|
||||
roc_str2.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
// Str.numberOfBytes
|
||||
|
||||
pub fn strNumberOfBytes(string: RocStr) callconv(.C) usize {
|
||||
return string.len();
|
||||
}
|
||||
|
||||
// Str.fromInt
|
||||
|
||||
pub fn strFromInt(int: i64) callconv(.C) RocStr {
|
||||
// prepare for having multiple integer types in the future
|
||||
return strFromIntHelp(i64, int);
|
||||
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
|
||||
pub fn strFromIntC(int: i64) callconv(.C) RocStr {
|
||||
return strFromInt(std.heap.c_allocator, 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
|
||||
comptime const size = comptime blk: {
|
||||
// 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;
|
||||
const result = std.fmt.bufPrint(&buf, "{}", .{int}) catch unreachable;
|
||||
|
||||
return RocStr.init(&buf, result.len);
|
||||
return RocStr.init(allocator, &buf, result.len);
|
||||
}
|
||||
|
||||
// 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 sliceStart_index: usize = 0;
|
||||
var slice_start_index: usize = 0;
|
||||
var str_index: usize = 0;
|
||||
|
||||
const str_bytes = string.asU8ptr();
|
||||
|
@ -279,10 +292,10 @@ pub fn strSplitInPlace(array: [*]RocStr, array_len: usize, string: RocStr, delim
|
|||
}
|
||||
|
||||
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);
|
||||
sliceStart_index = str_index + delimiter_len;
|
||||
array[ret_array_index] = RocStr.init(allocator, str_bytes + slice_start_index, segment_len);
|
||||
slice_start_index = str_index + delimiter_len;
|
||||
ret_array_index += 1;
|
||||
str_index += delimiter_len;
|
||||
} 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" {
|
||||
// Str.split "abc" "!" == [ "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 = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
|
||||
|
||||
var array: [1]RocStr = undefined;
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitInPlace(array_ptr, 1, str, delimiter);
|
||||
strSplitInPlace(testing.allocator, array_ptr, str, delimiter);
|
||||
|
||||
var expected = [1]RocStr{
|
||||
str,
|
||||
};
|
||||
|
||||
expectEqual(array.len, expected.len);
|
||||
expect(array[0].eq(expected[0]));
|
||||
|
||||
defer {
|
||||
for (array) |roc_str| {
|
||||
roc_str.deinit();
|
||||
roc_str.deinit(testing.allocator);
|
||||
}
|
||||
|
||||
for (expected) |roc_str| {
|
||||
roc_str.deinit();
|
||||
roc_str.deinit(testing.allocator);
|
||||
}
|
||||
|
||||
str.deinit(testing.allocator);
|
||||
delimiter.deinit(testing.allocator);
|
||||
}
|
||||
|
||||
expectEqual(array.len, expected.len);
|
||||
expect(array[0].eq(expected[0]));
|
||||
}
|
||||
|
||||
test "strSplitInPlace: empty end" {
|
||||
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 = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
|
||||
|
||||
const array_len: usize = 3;
|
||||
var array: [array_len]RocStr = [_]RocStr{
|
||||
|
@ -338,15 +356,28 @@ test "strSplitInPlace: empty end" {
|
|||
};
|
||||
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 two = RocStr.init("2", 1);
|
||||
const one = RocStr.init(testing.allocator, "1", 1);
|
||||
const two = RocStr.init(testing.allocator, "2", 1);
|
||||
|
||||
var expected = [3]RocStr{
|
||||
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);
|
||||
expect(array[0].eq(expected[0]));
|
||||
expect(array[1].eq(expected[1]));
|
||||
|
@ -355,10 +386,10 @@ test "strSplitInPlace: empty end" {
|
|||
|
||||
test "strSplitInPlace: delimiter on sides" {
|
||||
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 = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
|
||||
|
||||
const array_len: usize = 3;
|
||||
var array: [array_len]RocStr = [_]RocStr{
|
||||
|
@ -367,15 +398,28 @@ test "strSplitInPlace: delimiter on sides" {
|
|||
undefined,
|
||||
};
|
||||
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 = RocStr.init(ghi_arr, ghi_arr.len);
|
||||
const ghi = RocStr.init(testing.allocator, ghi_arr, ghi_arr.len);
|
||||
|
||||
var expected = [3]RocStr{
|
||||
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);
|
||||
expect(array[0].eq(expected[0]));
|
||||
expect(array[1].eq(expected[1]));
|
||||
|
@ -385,25 +429,38 @@ test "strSplitInPlace: delimiter on sides" {
|
|||
test "strSplitInPlace: three pieces" {
|
||||
// Str.split "a!b!c" "!" == [ "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 = RocStr.init(delimiter_arr, delimiter_arr.len);
|
||||
const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len);
|
||||
|
||||
const array_len: usize = 3;
|
||||
var array: [array_len]RocStr = undefined;
|
||||
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 b = RocStr.init("b", 1);
|
||||
const c = RocStr.init("c", 1);
|
||||
const a = RocStr.init(testing.allocator, "a", 1);
|
||||
const b = RocStr.init(testing.allocator, "b", 1);
|
||||
const c = RocStr.init(testing.allocator, "c", 1);
|
||||
|
||||
var expected_array = [array_len]RocStr{
|
||||
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);
|
||||
expect(array[0].eq(expected_array[0]));
|
||||
expect(array[1].eq(expected_array[1]));
|
||||
|
@ -459,13 +516,17 @@ test "countSegments: long delimiter" {
|
|||
// Str.split "str" "delimiter" == [ "str" ]
|
||||
// 1 segment
|
||||
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 = 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);
|
||||
|
||||
expectEqual(segments_count, 1);
|
||||
}
|
||||
|
||||
|
@ -473,10 +534,15 @@ test "countSegments: delimiter at start" {
|
|||
// Str.split "hello there" "hello" == [ "", " there" ]
|
||||
// 2 segments
|
||||
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 = 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);
|
||||
|
||||
|
@ -487,10 +553,15 @@ test "countSegments: delimiter interspered" {
|
|||
// Str.split "a!b!c" "!" == [ "a", "b", "c" ]
|
||||
// 3 segments
|
||||
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 = 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);
|
||||
|
||||
|
@ -499,7 +570,6 @@ test "countSegments: delimiter interspered" {
|
|||
|
||||
// Str.countGraphemeClusters
|
||||
const grapheme = @import("helpers/grapheme.zig");
|
||||
|
||||
pub fn countGraphemeClusters(string: RocStr) callconv(.C) usize {
|
||||
if (string.isEmpty()) {
|
||||
return 0;
|
||||
|
@ -545,40 +615,54 @@ test "countGraphemeClusters: empty string" {
|
|||
test "countGraphemeClusters: ascii characters" {
|
||||
const bytes_arr = "abcd";
|
||||
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);
|
||||
}
|
||||
|
||||
test "countGraphemeClusters: utf8 characters" {
|
||||
const bytes_arr = "ãxā";
|
||||
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);
|
||||
}
|
||||
|
||||
test "countGraphemeClusters: emojis" {
|
||||
const bytes_arr = "🤔🤔🤔";
|
||||
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);
|
||||
}
|
||||
|
||||
test "countGraphemeClusters: emojis and ut8 characters" {
|
||||
const bytes_arr = "🤔å🤔¥🤔ç";
|
||||
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);
|
||||
}
|
||||
|
||||
test "countGraphemeClusters: emojis, ut8, and ascii characters" {
|
||||
const bytes_arr = "6🤔å🤔e¥🤔çpp";
|
||||
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);
|
||||
}
|
||||
|
||||
// Str.startsWith
|
||||
|
||||
pub fn startsWith(string: RocStr, prefix: RocStr) callconv(.C) bool {
|
||||
const bytes_len = string.len();
|
||||
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" {
|
||||
const foo = RocStr.init("foo", 3);
|
||||
const fo = RocStr.init("fo", 2);
|
||||
const foo = RocStr.init(testing.allocator, "foo", 3);
|
||||
const fo = RocStr.init(testing.allocator, "fo", 2);
|
||||
expect(startsWith(foo, fo));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
test "startsWith: 12345678912345678910 starts with 123456789123456789" {
|
||||
const str = RocStr.init("12345678912345678910", 20);
|
||||
const prefix = RocStr.init("123456789123456789", 18);
|
||||
const str = RocStr.init(testing.allocator, "12345678912345678910", 20);
|
||||
defer str.deinit(testing.allocator);
|
||||
const prefix = RocStr.init(testing.allocator, "123456789123456789", 18);
|
||||
defer prefix.deinit(testing.allocator);
|
||||
|
||||
expect(startsWith(str, prefix));
|
||||
}
|
||||
|
||||
// Str.endsWith
|
||||
|
||||
pub fn endsWith(string: RocStr, suffix: RocStr) callconv(.C) bool {
|
||||
const bytes_len = string.len();
|
||||
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" {
|
||||
const foo = RocStr.init("foo", 3);
|
||||
const oo = RocStr.init("oo", 2);
|
||||
const foo = RocStr.init(testing.allocator, "foo", 3);
|
||||
const oo = RocStr.init(testing.allocator, "oo", 2);
|
||||
defer foo.deinit(testing.allocator);
|
||||
defer oo.deinit(testing.allocator);
|
||||
|
||||
expect(endsWith(foo, oo));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
test "endsWith: 12345678912345678910 ends with 345678912345678910" {
|
||||
const str = RocStr.init("12345678912345678910", 20);
|
||||
const suffix = RocStr.init("345678912345678910", 18);
|
||||
const str = RocStr.init(testing.allocator, "12345678912345678910", 20);
|
||||
const suffix = RocStr.init(testing.allocator, "345678912345678910", 18);
|
||||
defer str.deinit(testing.allocator);
|
||||
defer suffix.deinit(testing.allocator);
|
||||
|
||||
expect(endsWith(str, suffix));
|
||||
}
|
||||
|
||||
test "endsWith: hello world ends with world" {
|
||||
const str = RocStr.init("hello world", 11);
|
||||
const suffix = RocStr.init("world", 5);
|
||||
const str = RocStr.init(testing.allocator, "hello world", 11);
|
||||
const suffix = RocStr.init(testing.allocator, "world", 5);
|
||||
defer str.deinit(testing.allocator);
|
||||
defer suffix.deinit(testing.allocator);
|
||||
|
||||
expect(endsWith(str, suffix));
|
||||
}
|
||||
|
||||
// Str.concat
|
||||
|
||||
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(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();
|
||||
// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator
|
||||
pub fn strConcatC(ptr_size: u32, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) callconv(.C) RocStr {
|
||||
return strConcat(std.heap.c_allocator, ptr_size, result_in_place, arg1, arg2);
|
||||
}
|
||||
|
||||
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) {
|
||||
4 => strConcatHelp(i32, result_in_place, arg1, arg2),
|
||||
8 => strConcatHelp(i64, result_in_place, arg1, arg2),
|
||||
4 => strConcatHelp(allocator, i32, result_in_place, arg1, arg2),
|
||||
8 => strConcatHelp(allocator, i64, result_in_place, arg1, arg2),
|
||||
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()) {
|
||||
return cloneStr(T, result_in_place, arg2);
|
||||
return cloneStr(allocator, T, result_in_place, arg2);
|
||||
} else if (arg2.isEmpty()) {
|
||||
return cloneStr(T, result_in_place, arg1);
|
||||
return cloneStr(allocator, T, result_in_place, arg1);
|
||||
} else {
|
||||
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;
|
||||
|
||||
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);
|
||||
|
@ -775,12 +847,12 @@ const InPlace = packed enum(u8) {
|
|||
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()) {
|
||||
// just return the bytes
|
||||
return str;
|
||||
} 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 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;
|
||||
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) {
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::convert::AsRef;
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::{self};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::str;
|
||||
|
@ -11,65 +13,104 @@ fn main() {
|
|||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
// "." is relative to where "build.rs" is
|
||||
let src_path = Path::new(".").join("bitcode").join("src");
|
||||
let main_zig_path = src_path.join("main.zig");
|
||||
let build_script_dir_path = Path::new(".");
|
||||
|
||||
let src_path_str = src_path.to_str().expect("Invalid src path");
|
||||
println!("Building main.zig from: {}", src_path_str);
|
||||
let bitcode_path = build_script_dir_path.join("bitcode");
|
||||
|
||||
let zig_cache_dir = Path::new(&out_dir).join("zig-cache");
|
||||
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 src_path = bitcode_path.join("src");
|
||||
|
||||
let dest_ll_path = Path::new(&out_dir).join("builtins.ll");
|
||||
let dest_ll = dest_ll_path.to_str().expect("Invalid dest ir path");
|
||||
let emit_ir_arg = "-femit-llvm-ir=".to_owned() + dest_ll;
|
||||
println!("Compiling zig llvm-ir to: {}", dest_ll);
|
||||
let build_zig_path = bitcode_path.join("build.zig");
|
||||
let build_zig = build_zig_path.to_str().expect("Invalid build path");
|
||||
|
||||
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(
|
||||
"zig",
|
||||
&[
|
||||
"build-obj",
|
||||
main_zig_path.to_str().unwrap(),
|
||||
&emit_ir_arg,
|
||||
"-fno-emit-bin",
|
||||
"--strip",
|
||||
"-O",
|
||||
"ReleaseFast",
|
||||
"--cache-dir",
|
||||
zig_cache_dir_str,
|
||||
],
|
||||
&["build", "ir", "-Drelease=true", "--build-file", build_zig],
|
||||
);
|
||||
|
||||
let dest_bc_path = Path::new(&out_dir).join("builtins.bc");
|
||||
let dest_bc = dest_bc_path.to_str().expect("Invalid dest bc path");
|
||||
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={}", src_path_str);
|
||||
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
|
||||
I: IntoIterator<Item = S>,
|
||||
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 {
|
||||
Ok(output) => match output.status.success() {
|
||||
true => (),
|
||||
false => {
|
||||
let error_str = match str::from_utf8(&output.stderr) {
|
||||
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)
|
||||
// }
|
||||
|
|
|
@ -49,12 +49,7 @@ pub fn str_split<'a, 'ctx, 'env>(
|
|||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
ret_list_ptr_zig_rocstr,
|
||||
BasicValueEnum::IntValue(segment_count),
|
||||
str_i128.into(),
|
||||
delim_i128.into(),
|
||||
],
|
||||
&[ret_list_ptr_zig_rocstr, str_i128.into(), delim_i128.into()],
|
||||
&bitcode::STR_STR_SPLIT_IN_PLACE,
|
||||
);
|
||||
|
||||
|
|
|
@ -61,7 +61,11 @@ zerocopy = "0.3"
|
|||
env_logger = "0.7"
|
||||
futures = "0.3"
|
||||
wgpu_glyph = "0.10"
|
||||
bytemuck = "1.4"
|
||||
cgmath = "0.17.0"
|
||||
|
||||
[dependencies.bytemuck]
|
||||
version = "1.4"
|
||||
features = ["derive"]
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.5.1"
|
||||
|
|
101
editor/src/buffer.rs
Normal file
101
editor/src/buffer.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -11,21 +11,29 @@
|
|||
// re-enable this when working on performance optimizations than have it block PRs.
|
||||
#![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::vertex::Vertex;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use wgpu::util::DeviceExt;
|
||||
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;
|
||||
|
||||
pub mod ast;
|
||||
pub mod bucket;
|
||||
mod buffer;
|
||||
pub mod file;
|
||||
mod rect;
|
||||
pub mod text_state;
|
||||
mod util;
|
||||
mod vertex;
|
||||
|
||||
/// 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) };
|
||||
|
||||
// Initialize GPU
|
||||
let (device, queue) = futures::executor::block_on(async {
|
||||
let (gpu_device, cmd_queue) = futures::executor::block_on(async {
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
||||
|
@ -83,59 +91,26 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
|
|||
// Prepare swap chain
|
||||
let render_format = wgpu::TextureFormat::Bgra8UnormSrgb;
|
||||
let mut size = window.inner_size();
|
||||
let mut swap_chain = device.create_swap_chain(
|
||||
&surface,
|
||||
&wgpu::SwapChainDescriptor {
|
||||
|
||||
let swap_chain_descr = wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
format: render_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
//Immediate may cause tearing, change present_mode if this becomes a problem
|
||||
present_mode: wgpu::PresentMode::Immediate,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// Prepare Triangle Pipeline
|
||||
let triangle_vs_module =
|
||||
device.create_shader_module(wgpu::include_spirv!("shaders/rect.vert.spv"));
|
||||
let triangle_fs_module =
|
||||
device.create_shader_module(wgpu::include_spirv!("shaders/rect.frag.spv"));
|
||||
let mut swap_chain = gpu_device.create_swap_chain(&surface, &swap_chain_descr);
|
||||
|
||||
let triangle_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: None,
|
||||
bind_group_layouts: &[],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let triangle_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
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,
|
||||
});
|
||||
let rect_pipeline = make_rect_pipeline(&gpu_device, &swap_chain_descr);
|
||||
|
||||
// Prepare glyph_brush
|
||||
let inconsolata =
|
||||
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 mut text_state = String::new();
|
||||
|
@ -154,146 +129,246 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
|
||||
match event {
|
||||
winit::event::Event::WindowEvent {
|
||||
event: winit::event::WindowEvent::CloseRequested,
|
||||
Event::WindowEvent {
|
||||
event: event::WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = winit::event_loop::ControlFlow::Exit,
|
||||
winit::event::Event::WindowEvent {
|
||||
event: winit::event::WindowEvent::Resized(new_size),
|
||||
Event::WindowEvent {
|
||||
event: event::WindowEvent::Resized(new_size),
|
||||
..
|
||||
} => {
|
||||
size = new_size;
|
||||
|
||||
swap_chain = device.create_swap_chain(
|
||||
swap_chain = gpu_device.create_swap_chain(
|
||||
&surface,
|
||||
&wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
format: render_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
//Immediate may cause tearing, change present_mode if this becomes a problem
|
||||
present_mode: wgpu::PresentMode::Immediate,
|
||||
},
|
||||
);
|
||||
}
|
||||
winit::event::Event::WindowEvent {
|
||||
event: winit::event::WindowEvent::ReceivedCharacter(ch),
|
||||
Event::WindowEvent {
|
||||
event: event::WindowEvent::ReceivedCharacter(ch),
|
||||
..
|
||||
} => {
|
||||
match 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();
|
||||
update_text_state(&mut text_state, &ch);
|
||||
}
|
||||
'\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: winit::event::WindowEvent::KeyboardInput { input, .. },
|
||||
Event::WindowEvent {
|
||||
event: event::WindowEvent::KeyboardInput { input, .. },
|
||||
..
|
||||
} => {
|
||||
if let Some(virtual_keycode) = input.virtual_keycode {
|
||||
handle_keydown(input.state, virtual_keycode, keyboard_modifiers);
|
||||
}
|
||||
}
|
||||
winit::event::Event::WindowEvent {
|
||||
event: winit::event::WindowEvent::ModifiersChanged(modifiers),
|
||||
Event::WindowEvent {
|
||||
event: event::WindowEvent::ModifiersChanged(modifiers),
|
||||
..
|
||||
} => {
|
||||
keyboard_modifiers = modifiers;
|
||||
}
|
||||
winit::event::Event::MainEventsCleared => window.request_redraw(),
|
||||
winit::event::Event::RedrawRequested { .. } => {
|
||||
Event::MainEventsCleared => window.request_redraw(),
|
||||
Event::RedrawRequested { .. } => {
|
||||
// Get a command encoder for the current frame
|
||||
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
let mut encoder =
|
||||
gpu_device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("Redraw"),
|
||||
});
|
||||
|
||||
// Get the next frame
|
||||
let rect_buffers = create_rect_buffers(&gpu_device, &mut encoder);
|
||||
|
||||
let frame = swap_chain
|
||||
.get_current_frame()
|
||||
.expect("Failed to acquire next swap chain texture")
|
||||
.expect("Failed to acquire next SwapChainFrame")
|
||||
.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,
|
||||
},
|
||||
ops: wgpu::Operations::default(),
|
||||
}],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&triangle_pipeline);
|
||||
|
||||
render_pass.set_vertex_buffer(
|
||||
0, // The buffer slot to use for this vertex buffer.
|
||||
vertex_buffer.slice(..), // Use the entire buffer.
|
||||
);
|
||||
|
||||
render_pass.set_index_buffer(index_buffer.slice(..));
|
||||
|
||||
render_pass.draw_indexed(
|
||||
0..((&rect_index_buffers).len() as u32), // Draw all of the vertices from our test data.
|
||||
0, // Base Vertex
|
||||
0..1, // Instances
|
||||
);
|
||||
if rect_buffers.num_rects > 0 {
|
||||
render_pass.set_vertex_buffer(0, rect_buffers.vertex_buffer.slice(..));
|
||||
render_pass.set_index_buffer(rect_buffers.index_buffer.slice(..));
|
||||
render_pass.set_pipeline(&rect_pipeline);
|
||||
render_pass.draw_indexed(0..rect_buffers.num_rects, 0, 0..1);
|
||||
}
|
||||
|
||||
drop(render_pass);
|
||||
|
||||
draw_text(
|
||||
&gpu_device,
|
||||
&mut staging_belt,
|
||||
&mut encoder,
|
||||
&frame,
|
||||
&size,
|
||||
&text_state,
|
||||
&mut glyph_brush,
|
||||
);
|
||||
|
||||
staging_belt.finish();
|
||||
cmd_queue.submit(Some(encoder.finish()));
|
||||
|
||||
// Recall unused staging buffers
|
||||
use futures::task::SpawnExt;
|
||||
|
||||
local_spawner
|
||||
.spawn(staging_belt.recall())
|
||||
.expect("Recall staging belt");
|
||||
|
||||
local_pool.run_until_stalled();
|
||||
}
|
||||
_ => {
|
||||
*control_flow = winit::event_loop::ControlFlow::Wait;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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),
|
||||
|
@ -315,32 +390,31 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
|
|||
// Draw the text!
|
||||
glyph_brush
|
||||
.draw_queued(
|
||||
&device,
|
||||
&mut staging_belt,
|
||||
&mut encoder,
|
||||
gpu_device,
|
||||
staging_belt,
|
||||
encoder,
|
||||
&frame.view,
|
||||
size.width,
|
||||
size.height,
|
||||
)
|
||||
.expect("Draw queued");
|
||||
}
|
||||
|
||||
staging_belt.finish();
|
||||
queue.submit(Some(encoder.finish()));
|
||||
|
||||
// Recall unused staging buffers
|
||||
use futures::task::SpawnExt;
|
||||
|
||||
local_spawner
|
||||
.spawn(staging_belt.recall())
|
||||
.expect("Recall staging belt");
|
||||
|
||||
local_pool.run_until_stalled();
|
||||
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
|
||||
}
|
||||
_ => {
|
||||
*control_flow = winit::event_loop::ControlFlow::Wait;
|
||||
text_state.push(*received_char);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_keydown(
|
||||
|
|
|
@ -1,35 +1,8 @@
|
|||
use crate::vertex::Vertex;
|
||||
use cgmath::Vector2;
|
||||
|
||||
pub struct Rect {
|
||||
pub top: f32,
|
||||
pub left: f32,
|
||||
pub top_left_coords: Vector2<f32>,
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
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];
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#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;
|
||||
|
||||
// The actual color that is rendered to the screen based on the vertex.
|
||||
layout(location=0) out vec4 f_color;
|
||||
layout(location=0) out vec4 fColor;
|
||||
|
||||
void main() {
|
||||
f_color = vec4(color, 1.0);
|
||||
fColor = vec4(color, 1.0);
|
||||
}
|
|
@ -1,14 +1,13 @@
|
|||
#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,
|
||||
// as defined in the buffer descriptor for this shader.
|
||||
layout(location=0) in vec3 position;
|
||||
layout(location=1) in vec3 color;
|
||||
layout(location=0) in vec2 aPosition;
|
||||
layout(location=1) in vec3 aColor;
|
||||
|
||||
// Layout values labelled "out" send their data to the fragment shader.
|
||||
layout(location=0) out vec3 v_color;
|
||||
layout(location=0) out vec3 vColor;
|
||||
|
||||
void main() {
|
||||
v_color = color;
|
||||
gl_Position = vec4(position, 1.0);
|
||||
gl_Position = vec4(aPosition, 0, 1);
|
||||
vColor = aColor;
|
||||
}
|
5
editor/src/util.rs
Normal file
5
editor/src/util.rs
Normal 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()
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
// Taken from https://github.com/sotrh/learn-wgpu
|
||||
// by Benjamin Hansen, licensed under the MIT license
|
||||
use cgmath::Vector2;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Vertex {
|
||||
pub position: [f32; 3],
|
||||
#[allow(dead_code)]
|
||||
pub position: Vector2<f32>,
|
||||
pub color: [f32; 3],
|
||||
}
|
||||
|
||||
|
@ -9,17 +13,16 @@ unsafe impl bytemuck::Pod for Vertex {}
|
|||
unsafe impl bytemuck::Zeroable for Vertex {}
|
||||
|
||||
impl Vertex {
|
||||
// Defines how the shader will use this data structure.
|
||||
pub fn buffer_descriptor<'a>() -> wgpu::VertexBufferDescriptor<'a> {
|
||||
wgpu::VertexBufferDescriptor {
|
||||
stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
|
||||
pub const SIZE: wgpu::BufferAddress = std::mem::size_of::<Self>() as wgpu::BufferAddress;
|
||||
pub const DESC: wgpu::VertexBufferDescriptor<'static> = wgpu::VertexBufferDescriptor {
|
||||
stride: Self::SIZE,
|
||||
step_mode: wgpu::InputStepMode::Vertex,
|
||||
attributes: &[
|
||||
// position
|
||||
wgpu::VertexAttributeDescriptor {
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
format: wgpu::VertexFormat::Float3,
|
||||
format: wgpu::VertexFormat::Float2,
|
||||
},
|
||||
// color
|
||||
wgpu::VertexAttributeDescriptor {
|
||||
|
@ -28,6 +31,5 @@ impl Vertex {
|
|||
format: wgpu::VertexFormat::Float3,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
1
examples/balance/.gitignore
vendored
Normal file
1
examples/balance/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
main
|
|
@ -1,7 +1,7 @@
|
|||
app Main provides [ rocMain ] imports [ Effect ]
|
||||
app "main" imports [ Effect ] provides [ rocMain ] to "./platform"
|
||||
|
||||
|
||||
rocMain : Effect.Effect {} as Fx
|
||||
rocMain =
|
||||
when List.len (Str.split "hello" "JJJJ there") is
|
||||
_ -> Effect.putLine "Yay"
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
platform folkertdev/foo
|
||||
provides [ mainForHost ]
|
||||
requires { main : Effect {} }
|
||||
requires { rocMain : Effect {} }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects Effect
|
||||
{
|
||||
putChar : Int -> Effect {},
|
||||
|
@ -10,4 +12,4 @@ platform folkertdev/foo
|
|||
}
|
||||
|
||||
mainForHost : Effect {} as Fx
|
||||
mainForHost = main
|
||||
mainForHost = rocMain
|
||||
|
|
|
@ -7,16 +7,16 @@ use std::alloc::Layout;
|
|||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "Main_rocMain_1_exposed"]
|
||||
#[link_name = "roc__rocMain_1_exposed"]
|
||||
fn roc_main(output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "Main_rocMain_1_size"]
|
||||
#[link_name = "roc__rocMain_1_size"]
|
||||
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) -> ();
|
||||
|
||||
#[link_name = "Main_rocMain_1_Fx_size"]
|
||||
#[link_name = "roc__rocMain_1_Fx_size"]
|
||||
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>);
|
||||
|
||||
// match output.into() {
|
||||
// Ok(v) => v,
|
||||
// Err(e) => panic!("failed with {}", e),
|
||||
// }
|
||||
32
|
||||
match output.into() {
|
||||
Ok(_) => 0,
|
||||
Err(e) => panic!("failed with {}", e),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -96,7 +95,12 @@ pub fn rust_main() -> isize {
|
|||
|
||||
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) => {
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
|
|
1
examples/closure/.gitignore
vendored
Normal file
1
examples/closure/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
closure
|
|
@ -1,19 +1,21 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use roc_std::alloca;
|
||||
use roc_std::RocCallResult;
|
||||
use std::alloc::Layout;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "Main_makeClosure_1_exposed"]
|
||||
#[link_name = "roc__makeClosure_1_exposed"]
|
||||
fn make_closure(output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "Main_makeClosure_1_size"]
|
||||
#[link_name = "roc__makeClosure_1_size"]
|
||||
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) -> ();
|
||||
|
||||
#[link_name = "Main_makeClosure_1_MyClosure_size"]
|
||||
#[link_name = "roc__makeClosure_1_MyClosure_size"]
|
||||
fn size_MyClosure() -> i64;
|
||||
}
|
||||
|
||||
|
@ -65,7 +67,12 @@ pub fn rust_main() -> isize {
|
|||
|
||||
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) => {
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
|
@ -80,7 +87,6 @@ pub fn rust_main() -> isize {
|
|||
println!(
|
||||
"Roc closure took {:.4} ms to compute this answer: {:?}",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
// truncate the answer, so stdout is not swamped
|
||||
answer
|
||||
);
|
||||
|
||||
|
|
1
examples/effect/.gitignore
vendored
Normal file
1
examples/effect/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
effect-example
|
1
examples/shared-quicksort/.gitignore
vendored
Normal file
1
examples/shared-quicksort/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
quicksort
|
|
@ -1,9 +1,11 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use roc_std::RocCallResult;
|
||||
use roc_std::RocList;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "_quicksort_1_exposed"]
|
||||
#[link_name = "roc__quicksort_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ let
|
|||
linux-inputs =
|
||||
if isLinux then
|
||||
[
|
||||
valgrind
|
||||
vulkan-headers
|
||||
vulkan-loader
|
||||
vulkan-tools
|
||||
|
@ -71,7 +72,6 @@ let
|
|||
python3
|
||||
llvmPkgs.llvm
|
||||
llvmPkgs.clang
|
||||
valgrind
|
||||
pkg-config
|
||||
zig
|
||||
# llb deps
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue