mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 08:17:40 +03:00
Merge branch 'trunk' into editor_multiple_rects
This commit is contained in:
commit
553baea320
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,
|
||||
};
|
||||
|
||||
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);
|
||||
expect(array[0].eq(expected[0]));
|
||||
|
||||
for (array) |roc_str| {
|
||||
roc_str.deinit();
|
||||
}
|
||||
|
||||
for (expected) |roc_str| {
|
||||
roc_str.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
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,101 @@ 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();
|
||||
let path_ext = path.extension().unwrap();
|
||||
if path_ext == "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,
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user