Merge branch 'trunk' into editor_multiple_rects

This commit is contained in:
Anton-4 2020-12-05 10:34:16 +01:00
commit 553baea320
8 changed files with 310 additions and 190 deletions

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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