mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 23:31:12 +00:00
Merge remote-tracking branch 'origin/trunk' into task
This commit is contained in:
commit
4c2654d4ed
84 changed files with 3514 additions and 2186 deletions
697
Cargo.lock
generated
697
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -13,7 +13,7 @@ use target_lexicon::Triple;
|
|||
|
||||
fn report_timing(buf: &mut String, label: &str, duration: Duration) {
|
||||
buf.push_str(&format!(
|
||||
" {:.3} ms {}\n",
|
||||
" {:9.3} ms {}\n",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
label,
|
||||
));
|
||||
|
@ -45,6 +45,9 @@ pub fn build_file(
|
|||
src_dir.as_path(),
|
||||
subs_by_module,
|
||||
)?;
|
||||
|
||||
let path_to_platform = loaded.platform_path.clone();
|
||||
|
||||
let app_o_file = roc_file_path.with_file_name("roc_app.o");
|
||||
let buf = &mut String::with_capacity(1024);
|
||||
|
||||
|
@ -53,7 +56,14 @@ pub fn build_file(
|
|||
let module_name = loaded.interns.module_name(*module_id);
|
||||
|
||||
buf.push_str(" ");
|
||||
|
||||
if module_name.is_empty() {
|
||||
// the App module
|
||||
buf.push_str("Application Module");
|
||||
} else {
|
||||
buf.push_str(module_name);
|
||||
}
|
||||
|
||||
buf.push('\n');
|
||||
|
||||
report_timing(buf, "Read .roc file from disk", module_timing.read_roc_file);
|
||||
|
@ -81,15 +91,10 @@ pub fn build_file(
|
|||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
"\n\nCompilation finished! Here's how long each module took to compile:\n\n{}",
|
||||
buf
|
||||
);
|
||||
|
||||
let cwd = app_o_file.parent().unwrap();
|
||||
let binary_path = cwd.join(&*loaded.output_path); // TODO should join ".exe" on Windows
|
||||
|
||||
program::gen_from_mono_module(
|
||||
let code_gen_timing = program::gen_from_mono_module(
|
||||
&arena,
|
||||
loaded,
|
||||
roc_file_path,
|
||||
|
@ -99,6 +104,19 @@ pub fn build_file(
|
|||
emit_debug_info,
|
||||
);
|
||||
|
||||
buf.push('\n');
|
||||
buf.push_str(" ");
|
||||
buf.push_str("Code Generation");
|
||||
buf.push('\n');
|
||||
|
||||
report_timing(buf, "Generate LLVM IR", code_gen_timing.code_gen);
|
||||
report_timing(buf, "Emit .o file", code_gen_timing.emit_o_file);
|
||||
|
||||
println!(
|
||||
"\n\nCompilation finished! Here's how long each module took to compile:\n\n{}",
|
||||
buf
|
||||
);
|
||||
|
||||
println!("\nSuccess! 🎉\n\n\t➡ {}\n", app_o_file.display());
|
||||
|
||||
let compilation_end = compilation_start.elapsed().unwrap();
|
||||
|
@ -112,7 +130,9 @@ pub fn build_file(
|
|||
);
|
||||
|
||||
// Step 2: link the precompiled host and compiled app
|
||||
let host_input_path = cwd.join("platform").join("host.o");
|
||||
let mut host_input_path = PathBuf::from(cwd);
|
||||
host_input_path.push(&*path_to_platform);
|
||||
host_input_path.push("host.o");
|
||||
|
||||
// TODO we should no longer need to do this once we have platforms on
|
||||
// a package repository, as we can then get precompiled hosts from there.
|
||||
|
|
|
@ -54,12 +54,12 @@ mod repl_eval {
|
|||
|
||||
#[test]
|
||||
fn literal_0x0() {
|
||||
expect_success("0x0", "0 : Int");
|
||||
expect_success("0x0", "0 : I64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literal_0x42() {
|
||||
expect_success("0x42", "66 : Int");
|
||||
expect_success("0x42", "66 : I64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -79,7 +79,7 @@ mod repl_eval {
|
|||
|
||||
#[test]
|
||||
fn int_addition() {
|
||||
expect_success("0x1 + 2", "3 : Int");
|
||||
expect_success("0x1 + 2", "3 : I64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -89,7 +89,7 @@ mod repl_eval {
|
|||
|
||||
#[test]
|
||||
fn num_rem() {
|
||||
expect_success("299 % 10", "Ok 9 : Result Int [ DivByZero ]*");
|
||||
expect_success("299 % 10", "Ok 9 : Result I64 [ DivByZero ]*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -191,7 +191,7 @@ mod repl_eval {
|
|||
|
||||
#[test]
|
||||
fn str_count_graphemes() {
|
||||
expect_success("Str.countGraphemes \"å🤔\"", "2 : Int");
|
||||
expect_success("Str.countGraphemes \"å🤔\"", "2 : I64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -206,7 +206,7 @@ mod repl_eval {
|
|||
|
||||
#[test]
|
||||
fn literal_int_list() {
|
||||
expect_success("[ 0x1, 0x2, 0x3 ]", "[ 1, 2, 3 ] : List Int");
|
||||
expect_success("[ 0x1, 0x2, 0x3 ]", "[ 1, 2, 3 ] : List I64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -239,7 +239,7 @@ mod repl_eval {
|
|||
fn nested_int_list() {
|
||||
expect_success(
|
||||
r#"[ [ [ 4, 3, 2 ], [ 1, 0x0 ] ], [ [] ], [] ]"#,
|
||||
r#"[ [ [ 4, 3, 2 ], [ 1, 0 ] ], [ [] ], [] ] : List (List (List Int))"#,
|
||||
r#"[ [ [ 4, 3, 2 ], [ 1, 0 ] ], [ [] ], [] ] : List (List (List I64))"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -316,7 +316,7 @@ mod repl_eval {
|
|||
fn basic_2_field_i64_record() {
|
||||
expect_success(
|
||||
"{ foo: 0x4, bar: 0x2 }",
|
||||
"{ bar: 2, foo: 4 } : { bar : Int, foo : Int }",
|
||||
"{ bar: 2, foo: 4 } : { bar : I64, foo : I64 }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -340,7 +340,7 @@ mod repl_eval {
|
|||
fn basic_3_field_record() {
|
||||
expect_success(
|
||||
"{ foo: 4.1, bar: 2, baz: 0x5 }",
|
||||
"{ bar: 2, baz: 5, foo: 4.1 } : { bar : Num *, baz : Int, foo : F64 }",
|
||||
"{ bar: 2, baz: 5, foo: 4.1 } : { bar : Num *, baz : I64, foo : F64 }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -387,7 +387,7 @@ mod repl_eval {
|
|||
fn list_of_3_field_records() {
|
||||
expect_success(
|
||||
"[ { foo: 4.1, bar: 2, baz: 0x3 } ]",
|
||||
"[ { bar: 2, baz: 3, foo: 4.1 } ] : List { bar : Num *, baz : Int, foo : F64 }",
|
||||
"[ { bar: 2, baz: 3, foo: 4.1 } ] : List { bar : Num *, baz : I64, foo : F64 }",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ pub fn rebuild_host(host_input_path: &Path) {
|
|||
.output()
|
||||
.unwrap();
|
||||
|
||||
validate_output("host.rs", "cargo build --release", output);
|
||||
validate_output("src/lib.rs", "cargo build --release", output);
|
||||
|
||||
let output = Command::new("ld")
|
||||
.env_clear()
|
||||
|
|
|
@ -7,8 +7,15 @@ use roc_gen::llvm::build::{build_proc, build_proc_header, module_from_builtins,
|
|||
use roc_load::file::MonomorphizedModule;
|
||||
use roc_mono::layout::LayoutIds;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::{Duration, SystemTime};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct CodeGenTiming {
|
||||
pub code_gen: Duration,
|
||||
pub emit_o_file: Duration,
|
||||
}
|
||||
|
||||
// TODO how should imported modules factor into this? What if those use builtins too?
|
||||
// TODO this should probably use more helper functions
|
||||
// TODO make this polymorphic in the llvm functions so it can be reused for another backend.
|
||||
|
@ -21,11 +28,13 @@ pub fn gen_from_mono_module(
|
|||
app_o_file: &Path,
|
||||
opt_level: OptLevel,
|
||||
emit_debug_info: bool,
|
||||
) {
|
||||
) -> CodeGenTiming {
|
||||
use roc_reporting::report::{
|
||||
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
||||
};
|
||||
|
||||
let code_gen_start = SystemTime::now();
|
||||
|
||||
for (home, (module_path, src)) in loaded.sources {
|
||||
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||
let palette = DEFAULT_PALETTE;
|
||||
|
@ -160,6 +169,9 @@ pub fn gen_from_mono_module(
|
|||
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
let code_gen = code_gen_start.elapsed().unwrap();
|
||||
let emit_o_file_start = SystemTime::now();
|
||||
|
||||
// annotate the LLVM IR output with debug info
|
||||
// so errors are reported with the line number of the LLVM source
|
||||
if emit_debug_info {
|
||||
|
@ -232,6 +244,13 @@ pub fn gen_from_mono_module(
|
|||
.write_to_file(&env.module, FileType::Object, &app_o_file)
|
||||
.expect("Writing .o file failed");
|
||||
}
|
||||
|
||||
let emit_o_file = emit_o_file_start.elapsed().unwrap();
|
||||
|
||||
CodeGenTiming {
|
||||
emit_o_file,
|
||||
code_gen,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FunctionIterator<'ctx> {
|
||||
|
|
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
|
||||
|
|
|
@ -4,7 +4,7 @@ const expectEqual = std.testing.expectEqual;
|
|||
|
||||
// This whole module is a translation of grapheme breaks from
|
||||
// the https://github.com/JuliaStrings/utf8proc library.
|
||||
// Thanks so much to those developer!
|
||||
// Thanks so much to those developers!
|
||||
//
|
||||
// The only function this file exposes is `isGraphemeBreak`
|
||||
//
|
||||
|
|
|
@ -15,25 +15,25 @@ 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
|
||||
fn exportBuiltinFn(comptime fn_target: anytype, comptime fn_name: []const u8) void {
|
||||
@export(fn_target, .{ .name = "roc_builtins." ++ fn_name, .linkage = .Strong });
|
||||
fn exportBuiltinFn(comptime func: anytype, comptime func_name: []const u8) void {
|
||||
@export(func, .{ .name = "roc_builtins." ++ func_name, .linkage = .Strong });
|
||||
}
|
||||
fn exportNumFn(comptime fn_target: anytype, comptime fn_name: []const u8) void {
|
||||
exportBuiltinFn(fn_target, "num." ++ fn_name);
|
||||
fn exportNumFn(comptime func: anytype, comptime func_name: []const u8) void {
|
||||
exportBuiltinFn(func, "num." ++ func_name);
|
||||
}
|
||||
fn exportStrFn(comptime fn_target: anytype, comptime fn_name: []const u8) void {
|
||||
exportBuiltinFn(fn_target, "str." ++ fn_name);
|
||||
fn exportStrFn(comptime func: anytype, comptime func_name: []const u8) void {
|
||||
exportBuiltinFn(func, "str." ++ func_name);
|
||||
}
|
||||
|
||||
// Run all tests in imported modules
|
||||
|
|
|
@ -1,36 +1,35 @@
|
|||
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 {
|
||||
const rocStrSize = @sizeOf(RocStr);
|
||||
pub fn init(allocator: *Allocator, bytes_ptr: [*]const u8, length: usize) RocStr {
|
||||
const roc_str_size = @sizeOf(RocStr);
|
||||
|
||||
if (length < rocStrSize) {
|
||||
if (length < roc_str_size) {
|
||||
var ret_small_str = RocStr.empty();
|
||||
const target_ptr = @ptrToInt(&ret_small_str);
|
||||
var index: u8 = 0;
|
||||
|
||||
// TODO isn't there a way to bulk-zero data in Zig?
|
||||
// Zero out the data, just to be safe
|
||||
while (index < rocStrSize) {
|
||||
while (index < roc_str_size) {
|
||||
var offset_ptr = @intToPtr(*u8, target_ptr + index);
|
||||
offset_ptr.* = 0;
|
||||
index += 1;
|
||||
|
@ -40,19 +39,19 @@ 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;
|
||||
}
|
||||
|
||||
// set the final byte to be the length
|
||||
const final_byte_ptr = @intToPtr(*u8, target_ptr + rocStrSize - 1);
|
||||
const final_byte_ptr = @intToPtr(*u8, target_ptr + roc_str_size - 1);
|
||||
final_byte_ptr.* = @truncate(u8, length) ^ 0b10000000;
|
||||
|
||||
return ret_small_str;
|
||||
} else {
|
||||
var result = allocate_str(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;
|
||||
}
|
||||
|
@ -61,25 +60,28 @@ const RocStr = extern struct {
|
|||
// This takes ownership of 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 withCapacity(length: usize) RocStr {
|
||||
const rocStrSize = @sizeOf(RocStr);
|
||||
const roc_str_size = @sizeOf(RocStr);
|
||||
|
||||
if (length < rocStrSize) {
|
||||
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 drop(self: RocStr) void {
|
||||
if (!self.is_small_str()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,8 +104,8 @@ const RocStr = extern struct {
|
|||
|
||||
const self_u8_ptr: [*]const u8 = @ptrCast([*]const u8, &self);
|
||||
const other_u8_ptr: [*]const u8 = @ptrCast([*]const u8, &other);
|
||||
const self_bytes: [*]const u8 = if (self.is_small_str() or self.is_empty()) self_u8_ptr else self_bytes_ptr orelse unreachable;
|
||||
const other_bytes: [*]const u8 = if (other.is_small_str() or other.is_empty()) other_u8_ptr else other_bytes_ptr orelse unreachable;
|
||||
const self_bytes: [*]const u8 = if (self.isSmallStr() or self.isEmpty()) self_u8_ptr else self_bytes_ptr orelse unreachable;
|
||||
const other_bytes: [*]const u8 = if (other.isSmallStr() or other.isEmpty()) other_u8_ptr else other_bytes_ptr orelse unreachable;
|
||||
|
||||
var index: usize = 0;
|
||||
|
||||
|
@ -120,7 +122,7 @@ const RocStr = extern struct {
|
|||
return true;
|
||||
}
|
||||
|
||||
pub fn is_small_str(self: RocStr) bool {
|
||||
pub fn isSmallStr(self: RocStr) bool {
|
||||
return @bitCast(isize, self.str_len) < 0;
|
||||
}
|
||||
|
||||
|
@ -132,17 +134,17 @@ const RocStr = extern struct {
|
|||
|
||||
// Since this conditional would be prone to branch misprediction,
|
||||
// make sure it will compile to a cmov.
|
||||
return if (self.is_small_str()) small_len else big_len;
|
||||
return if (self.isSmallStr()) small_len else big_len;
|
||||
}
|
||||
|
||||
pub fn is_empty(self: RocStr) bool {
|
||||
pub fn isEmpty(self: RocStr) bool {
|
||||
return self.len() == 0;
|
||||
}
|
||||
|
||||
pub fn as_u8_ptr(self: RocStr) [*]u8 {
|
||||
pub fn asU8ptr(self: RocStr) [*]u8 {
|
||||
const if_small = &@bitCast([16]u8, self);
|
||||
const if_big = @ptrCast([*]u8, self.str_bytes);
|
||||
return if (self.is_small_str() or self.is_empty()) if_small else if_big;
|
||||
return if (self.isSmallStr() or self.isEmpty()) if_small else if_big;
|
||||
}
|
||||
|
||||
// Given a pointer to some bytes, write the first (len) bytes of this
|
||||
|
@ -160,7 +162,7 @@ const RocStr = extern struct {
|
|||
|
||||
// Since this conditional would be prone to branch misprediction,
|
||||
// make sure it will compile to a cmov.
|
||||
const src: [*]u8 = if (self.is_small_str()) small_src else big_src;
|
||||
const src: [*]u8 = if (self.isSmallStr()) small_src else big_src;
|
||||
|
||||
@memcpy(dest, src, len);
|
||||
}
|
||||
|
@ -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.drop();
|
||||
roc_str2.drop();
|
||||
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.drop();
|
||||
roc_str2.drop();
|
||||
}
|
||||
|
||||
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.drop();
|
||||
roc_str2.drop();
|
||||
}
|
||||
};
|
||||
|
||||
// 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,20 +253,24 @@ 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.as_u8_ptr();
|
||||
const str_bytes = string.asU8ptr();
|
||||
const str_len = string.len();
|
||||
|
||||
const delimiter_bytes_ptrs = delimiter.as_u8_ptr();
|
||||
const delimiter_bytes_ptrs = delimiter.asU8ptr();
|
||||
const delimiter_len = delimiter.len();
|
||||
|
||||
if (str_len > delimiter_len) {
|
||||
|
@ -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.drop();
|
||||
roc_str.deinit(testing.allocator);
|
||||
}
|
||||
|
||||
for (expected) |roc_str| {
|
||||
roc_str.drop();
|
||||
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]));
|
||||
|
@ -415,10 +472,10 @@ test "strSplitInPlace: three pieces" {
|
|||
// needs to be broken into, so that we can allocate a array
|
||||
// of that size. It always returns at least 1.
|
||||
pub fn countSegments(string: RocStr, delimiter: RocStr) callconv(.C) usize {
|
||||
const str_bytes = string.as_u8_ptr();
|
||||
const str_bytes = string.asU8ptr();
|
||||
const str_len = string.len();
|
||||
|
||||
const delimiter_bytes_ptrs = delimiter.as_u8_ptr();
|
||||
const delimiter_bytes_ptrs = delimiter.asU8ptr();
|
||||
const delimiter_len = delimiter.len();
|
||||
|
||||
var count: usize = 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,14 +570,13 @@ test "countSegments: delimiter interspered" {
|
|||
|
||||
// Str.countGraphemeClusters
|
||||
const grapheme = @import("helpers/grapheme.zig");
|
||||
|
||||
pub fn countGraphemeClusters(string: RocStr) callconv(.C) usize {
|
||||
if (string.is_empty()) {
|
||||
if (string.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const bytes_len = string.len();
|
||||
const bytes_ptr = string.as_u8_ptr();
|
||||
const bytes_ptr = string.asU8ptr();
|
||||
|
||||
var bytes = bytes_ptr[0..bytes_len];
|
||||
var iter = (unicode.Utf8View.init(bytes) catch unreachable).iterator();
|
||||
|
@ -535,7 +605,7 @@ pub fn countGraphemeClusters(string: RocStr) callconv(.C) usize {
|
|||
return count;
|
||||
}
|
||||
|
||||
fn roc_str_from_literal(bytes_arr: *const []u8) RocStr {}
|
||||
fn rocStrFromLiteral(bytes_arr: *const []u8) RocStr {}
|
||||
|
||||
test "countGraphemeClusters: empty string" {
|
||||
const count = countGraphemeClusters(RocStr.empty());
|
||||
|
@ -545,46 +615,60 @@ 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.as_u8_ptr();
|
||||
const bytes_ptr = string.asU8ptr();
|
||||
|
||||
const prefix_len = prefix.len();
|
||||
const prefix_ptr = prefix.as_u8_ptr();
|
||||
const prefix_ptr = prefix.asU8ptr();
|
||||
|
||||
if (prefix_len > bytes_len) {
|
||||
return false;
|
||||
|
@ -602,31 +686,33 @@ 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.as_u8_ptr();
|
||||
const bytes_ptr = string.asU8ptr();
|
||||
|
||||
const suffix_len = suffix.len();
|
||||
const suffix_ptr = suffix.as_u8_ptr();
|
||||
const suffix_ptr = suffix.asU8ptr();
|
||||
|
||||
if (suffix_len > bytes_len) {
|
||||
return false;
|
||||
|
@ -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.drop();
|
||||
roc_str2.drop();
|
||||
roc_str3.drop();
|
||||
result.drop();
|
||||
// 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 {
|
||||
if (arg1.is_empty()) {
|
||||
return cloneStr(T, result_in_place, arg2);
|
||||
} else if (arg2.is_empty()) {
|
||||
return cloneStr(T, result_in_place, arg1);
|
||||
fn strConcatHelp(allocator: *Allocator, comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr {
|
||||
if (arg1.isEmpty()) {
|
||||
return cloneStr(allocator, T, result_in_place, arg2);
|
||||
} else if (arg2.isEmpty()) {
|
||||
return cloneStr(allocator, T, result_in_place, arg1);
|
||||
} else {
|
||||
const combined_length = arg1.len() + arg2.len();
|
||||
|
||||
|
@ -716,12 +788,12 @@ 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 = allocate_str(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_big = @ptrCast([*]u8, arg1.str_bytes);
|
||||
const old_bytes = if (arg1.is_small_str()) old_if_small else old_if_big;
|
||||
const old_bytes = if (arg1.isSmallStr()) old_if_small else old_if_big;
|
||||
|
||||
const new_bytes: [*]u8 = @ptrCast([*]u8, result.str_bytes);
|
||||
|
||||
|
@ -731,7 +803,7 @@ fn strConcatHelp(comptime T: type, result_in_place: InPlace, arg1: RocStr, arg2:
|
|||
{
|
||||
const old_if_small = &@bitCast([16]u8, arg2);
|
||||
const old_if_big = @ptrCast([*]u8, arg2.str_bytes);
|
||||
const old_bytes = if (arg2.is_small_str()) old_if_small else old_if_big;
|
||||
const old_bytes = if (arg2.isSmallStr()) old_if_small else old_if_big;
|
||||
|
||||
const new_bytes = @ptrCast([*]u8, result.str_bytes) + arg1.len();
|
||||
|
||||
|
@ -775,12 +847,12 @@ const InPlace = packed enum(u8) {
|
|||
Clone,
|
||||
};
|
||||
|
||||
fn cloneStr(comptime T: type, in_place: InPlace, str: RocStr) RocStr {
|
||||
if (str.is_small_str() or str.is_empty()) {
|
||||
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 = allocate_str(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 allocate_str(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);
|
||||
|
@ -802,10 +874,39 @@ fn allocate_str(comptime T: type, in_place: InPlace, number_of_chars: u64) RocSt
|
|||
}
|
||||
|
||||
var first_element = @ptrCast([*]align(@alignOf(T)) u8, new_bytes);
|
||||
first_element += 8;
|
||||
first_element += @sizeOf(usize);
|
||||
|
||||
return RocStr{
|
||||
.str_bytes = first_element,
|
||||
.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)
|
||||
// }
|
||||
|
|
|
@ -1207,7 +1207,7 @@ fn int_type(u: VarId) -> SolvedType {
|
|||
vec![
|
||||
flex(u),
|
||||
SolvedType::Alias(
|
||||
Symbol::NUM_INT,
|
||||
Symbol::NUM_I64,
|
||||
Vec::new(),
|
||||
Box::new(builtin_aliases::num_type(SolvedType::Apply(
|
||||
Symbol::ATTR_ATTR,
|
||||
|
|
|
@ -172,9 +172,9 @@ pub struct Field {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Recursive {
|
||||
Recursive,
|
||||
TailRecursive,
|
||||
NotRecursive,
|
||||
NotRecursive = 0,
|
||||
Recursive = 1,
|
||||
TailRecursive = 2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
|
@ -97,6 +97,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
let first_char = ident.as_inline_str().chars().next().unwrap();
|
||||
|
||||
if first_char.is_lowercase() {
|
||||
// this is a value definition
|
||||
let expr_var = var_store.fresh();
|
||||
|
||||
match scope.import(ident, symbol, region) {
|
||||
|
@ -116,7 +117,10 @@ pub fn canonicalize_module_defs<'a>(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
panic!("TODO add type aliases to type alias dictionary, based on exposed types");
|
||||
// This is a type alias
|
||||
|
||||
// the should already be added to the scope when this module is canonicalized
|
||||
debug_assert!(scope.contains_alias(symbol));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -192,4 +192,8 @@ impl Scope {
|
|||
|
||||
self.aliases.insert(name, alias);
|
||||
}
|
||||
|
||||
pub fn contains_alias(&mut self, name: Symbol) -> bool {
|
||||
self.aliases.contains_key(&name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ mod test_can {
|
|||
assert_eq!(expected, actual);
|
||||
}
|
||||
actual => {
|
||||
panic!("Expected an Int, but got: {:?}", actual);
|
||||
panic!("Expected an I64, but got: {:?}", actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ mod test_can {
|
|||
fn correct_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int -> Int
|
||||
f : I64 -> I64
|
||||
f = \ a -> a
|
||||
|
||||
f
|
||||
|
@ -265,7 +265,7 @@ mod test_can {
|
|||
fn correct_annotated_body_with_comments() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int -> Int # comment
|
||||
f : I64 -> I64 # comment
|
||||
f = \ a -> a
|
||||
|
||||
f
|
||||
|
@ -281,7 +281,7 @@ mod test_can {
|
|||
fn name_mismatch_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int -> Int
|
||||
f : I64 -> I64
|
||||
g = \ a -> a
|
||||
|
||||
g
|
||||
|
@ -307,7 +307,7 @@ mod test_can {
|
|||
fn name_mismatch_annotated_body_with_comment() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int -> Int # comment
|
||||
f : I64 -> I64 # comment
|
||||
g = \ a -> a
|
||||
|
||||
g
|
||||
|
@ -333,7 +333,7 @@ mod test_can {
|
|||
fn separated_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int -> Int
|
||||
f : I64 -> I64
|
||||
|
||||
f = \ a -> a
|
||||
|
||||
|
@ -354,7 +354,7 @@ mod test_can {
|
|||
fn separated_annotated_body_with_comment() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int -> Int
|
||||
f : I64 -> I64
|
||||
# comment
|
||||
f = \ a -> a
|
||||
|
||||
|
@ -375,9 +375,9 @@ mod test_can {
|
|||
fn shadowed_annotation() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int -> Int
|
||||
f : I64 -> I64
|
||||
|
||||
f : Int -> Int
|
||||
f : I64 -> I64
|
||||
|
||||
f
|
||||
"#
|
||||
|
@ -397,7 +397,7 @@ mod test_can {
|
|||
fn correct_nested_unannotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int
|
||||
f : I64
|
||||
f =
|
||||
g = 42
|
||||
|
||||
|
@ -416,9 +416,9 @@ mod test_can {
|
|||
fn correct_nested_annotated_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int
|
||||
f : I64
|
||||
f =
|
||||
g : Int
|
||||
g : I64
|
||||
g = 42
|
||||
|
||||
g + 1
|
||||
|
@ -436,11 +436,11 @@ mod test_can {
|
|||
fn correct_nested_body_annotated_multiple_lines() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int
|
||||
f : I64
|
||||
f =
|
||||
g : Int
|
||||
g : I64
|
||||
g = 42
|
||||
h : Int
|
||||
h : I64
|
||||
h = 5
|
||||
z = 4
|
||||
g + h + z
|
||||
|
@ -458,10 +458,10 @@ mod test_can {
|
|||
fn correct_nested_body_unannotated_multiple_lines() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int
|
||||
f : I64
|
||||
f =
|
||||
g = 42
|
||||
h : Int
|
||||
h : I64
|
||||
h = 5
|
||||
z = 4
|
||||
g + h + z
|
||||
|
@ -478,7 +478,7 @@ mod test_can {
|
|||
fn correct_double_nested_body() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
f : Int
|
||||
f : I64
|
||||
f =
|
||||
g =
|
||||
h = 42
|
||||
|
@ -499,7 +499,7 @@ mod test_can {
|
|||
fn annotation_followed_with_unrelated_affectation() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
F : Int
|
||||
F : I64
|
||||
|
||||
x = 1
|
||||
|
||||
|
@ -520,9 +520,9 @@ mod test_can {
|
|||
fn two_annotations_followed_with_unrelated_affectation() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
G : Int
|
||||
G : I64
|
||||
|
||||
F : Int
|
||||
F : I64
|
||||
|
||||
x = 1
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ pub fn num_floatingpoint() -> Type {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn num_int() -> Type {
|
||||
Type::Alias(Symbol::NUM_INT, vec![], Box::new(num_num(num_integer())))
|
||||
Type::Alias(Symbol::NUM_I64, vec![], Box::new(num_num(num_integer())))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -762,8 +762,8 @@ mod test_fmt {
|
|||
expr_formats_to(
|
||||
indoc!(
|
||||
r#"
|
||||
f: { y : Int,
|
||||
x : Int ,
|
||||
f: { y : I64,
|
||||
x : I64 ,
|
||||
}
|
||||
|
||||
f"#
|
||||
|
@ -772,8 +772,8 @@ mod test_fmt {
|
|||
r#"
|
||||
f :
|
||||
{
|
||||
y : Int,
|
||||
x : Int,
|
||||
y : I64,
|
||||
x : I64,
|
||||
}
|
||||
|
||||
f"#
|
||||
|
@ -787,8 +787,8 @@ mod test_fmt {
|
|||
r#"
|
||||
f :
|
||||
{
|
||||
y : Int,
|
||||
x : Int,
|
||||
y : I64,
|
||||
x : I64,
|
||||
}
|
||||
|
||||
f"#
|
||||
|
@ -800,7 +800,7 @@ mod test_fmt {
|
|||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
f :
|
||||
Int
|
||||
I64
|
||||
|
||||
f"#
|
||||
));
|
||||
|
@ -880,7 +880,7 @@ mod test_fmt {
|
|||
r#"
|
||||
f :
|
||||
{
|
||||
x: Int # comment 1
|
||||
x: I64 # comment 1
|
||||
,
|
||||
# comment 2
|
||||
}
|
||||
|
@ -891,7 +891,7 @@ mod test_fmt {
|
|||
r#"
|
||||
f :
|
||||
{
|
||||
x : Int,
|
||||
x : I64,
|
||||
# comment 1
|
||||
# comment 2
|
||||
}
|
||||
|
@ -2458,7 +2458,7 @@ mod test_fmt {
|
|||
fn record_type() {
|
||||
expr_formats_same(indoc!(
|
||||
r#"
|
||||
f : { foo : Int }
|
||||
f : { foo : I64 }
|
||||
f = { foo: 1000 }
|
||||
|
||||
a
|
||||
|
@ -2509,11 +2509,11 @@ mod test_fmt {
|
|||
// r#"
|
||||
// f :
|
||||
// Result a
|
||||
// { x : Int
|
||||
// { x : I64
|
||||
// , y : Float
|
||||
// }
|
||||
// c
|
||||
// -> Int
|
||||
// -> I64
|
||||
// f =
|
||||
// \_ -> 4
|
||||
// "#
|
||||
|
|
|
@ -1230,7 +1230,20 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
use roc_mono::ir::Stmt::*;
|
||||
|
||||
match stmt {
|
||||
Let(symbol, expr, layout, cont) => {
|
||||
Let(first_symbol, first_expr, first_layout, mut cont) => {
|
||||
let mut queue = Vec::new_in(env.arena);
|
||||
|
||||
queue.push((first_symbol, first_expr, first_layout));
|
||||
|
||||
while let Let(symbol, expr, layout, new_cont) = cont {
|
||||
queue.push((symbol, expr, layout));
|
||||
|
||||
cont = new_cont;
|
||||
}
|
||||
|
||||
let mut stack = Vec::with_capacity_in(queue.len(), env.arena);
|
||||
|
||||
for (symbol, expr, layout) in queue {
|
||||
let context = &env.context;
|
||||
|
||||
let val = build_exp_expr(env, layout_ids, &scope, parent, layout, &expr);
|
||||
|
@ -1248,8 +1261,12 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
} else {
|
||||
basic_type_from_layout(env.arena, context, &layout, env.ptr_bytes)
|
||||
};
|
||||
let alloca =
|
||||
create_entry_block_alloca(env, parent, expr_bt, symbol.ident_string(&env.interns));
|
||||
let alloca = create_entry_block_alloca(
|
||||
env,
|
||||
parent,
|
||||
expr_bt,
|
||||
symbol.ident_string(&env.interns),
|
||||
);
|
||||
|
||||
env.builder.build_store(alloca, val);
|
||||
|
||||
|
@ -1261,8 +1278,14 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
// scope = scope.clone();
|
||||
|
||||
scope.insert(*symbol, (layout.clone(), alloca));
|
||||
stack.push(*symbol);
|
||||
}
|
||||
|
||||
let result = build_exp_stmt(env, layout_ids, scope, parent, cont);
|
||||
scope.remove(symbol);
|
||||
|
||||
for symbol in stack {
|
||||
scope.remove(&symbol);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
initThrees : List Int
|
||||
initThrees : List I64
|
||||
initThrees =
|
||||
[]
|
||||
|
||||
|
@ -204,7 +204,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
init : List Int
|
||||
init : List I64
|
||||
init =
|
||||
[]
|
||||
|
||||
|
@ -251,7 +251,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
empty : List Int
|
||||
empty : List I64
|
||||
empty =
|
||||
[]
|
||||
|
||||
|
@ -330,7 +330,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
empty : List Int
|
||||
empty : List I64
|
||||
empty =
|
||||
[]
|
||||
|
||||
|
@ -347,7 +347,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
alwaysTrue : Int -> Bool
|
||||
alwaysTrue : I64 -> Bool
|
||||
alwaysTrue = \_ ->
|
||||
True
|
||||
|
||||
|
@ -365,11 +365,11 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
alwaysTrue : Int -> Bool
|
||||
alwaysTrue : I64 -> Bool
|
||||
alwaysTrue = \_ ->
|
||||
True
|
||||
|
||||
oneThroughEight : List Int
|
||||
oneThroughEight : List I64
|
||||
oneThroughEight =
|
||||
[1,2,3,4,5,6,7,8]
|
||||
|
||||
|
@ -386,7 +386,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
alwaysFalse : Int -> Bool
|
||||
alwaysFalse : I64 -> Bool
|
||||
alwaysFalse = \_ ->
|
||||
False
|
||||
|
||||
|
@ -403,7 +403,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
intIsLessThanThree : Int -> Bool
|
||||
intIsLessThanThree : I64 -> Bool
|
||||
intIsLessThanThree = \i ->
|
||||
i < 3
|
||||
|
||||
|
@ -440,7 +440,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
empty : List Int
|
||||
empty : List I64
|
||||
empty =
|
||||
[]
|
||||
|
||||
|
@ -457,7 +457,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
nonEmpty : List Int
|
||||
nonEmpty : List I64
|
||||
nonEmpty =
|
||||
[ 1 ]
|
||||
|
||||
|
@ -474,7 +474,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
nonEmpty : List Int
|
||||
nonEmpty : List I64
|
||||
nonEmpty =
|
||||
[ 1 ]
|
||||
|
||||
|
@ -491,7 +491,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
nonEmpty : List Int
|
||||
nonEmpty : List I64
|
||||
nonEmpty =
|
||||
[ 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 ]
|
||||
|
||||
|
@ -510,7 +510,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
nonEmpty : List Int
|
||||
nonEmpty : List I64
|
||||
nonEmpty =
|
||||
[ 1, 1, -4, 1, 2 ]
|
||||
|
||||
|
@ -528,11 +528,11 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
nonEmpty : List Int
|
||||
nonEmpty : List I64
|
||||
nonEmpty =
|
||||
[ 2, 2, -4, 2, 3 ]
|
||||
|
||||
greaterThanOne : Int -> Bool
|
||||
greaterThanOne : I64 -> Bool
|
||||
greaterThanOne = \i ->
|
||||
i > 1
|
||||
|
||||
|
@ -730,7 +730,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
emptyList : List Int
|
||||
emptyList : List I64
|
||||
emptyList =
|
||||
[]
|
||||
|
||||
|
@ -752,11 +752,11 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
firstList : List Int
|
||||
firstList : List I64
|
||||
firstList =
|
||||
[]
|
||||
|
||||
secondList : List Int
|
||||
secondList : List I64
|
||||
secondList =
|
||||
[]
|
||||
|
||||
|
@ -778,11 +778,11 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
firstList : List Int
|
||||
firstList : List I64
|
||||
firstList =
|
||||
[]
|
||||
|
||||
secondList : List Int
|
||||
secondList : List I64
|
||||
secondList =
|
||||
[]
|
||||
|
||||
|
@ -1229,7 +1229,7 @@ mod gen_list {
|
|||
app "quicksort" provides [ main ] to "./platform"
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -1254,7 +1254,7 @@ mod gen_list {
|
|||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// swap : Int, Int, List a -> List a
|
||||
// swap : I64, I64, List a -> List a
|
||||
// swap = \i, j, list ->
|
||||
// when Pair (List.get list i) (List.get list j) is
|
||||
// Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -1264,7 +1264,7 @@ mod gen_list {
|
|||
//
|
||||
// _ ->
|
||||
// []
|
||||
// partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
// partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
// partition = \low, high, initialList ->
|
||||
// when List.get initialList high is
|
||||
// Ok pivot ->
|
||||
|
@ -1276,7 +1276,7 @@ mod gen_list {
|
|||
// Pair (low - 1) initialList
|
||||
//
|
||||
//
|
||||
// partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
|
||||
// partitionHelp : I64, I64, List (Num a), I64, I64 -> [ Pair I64 (List (Num a)) ]
|
||||
// partitionHelp = \i, j, list, high, pivot ->
|
||||
// if j < high then
|
||||
// when List.get list j is
|
||||
|
@ -1306,7 +1306,7 @@ mod gen_list {
|
|||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// swap : Int, Int, List a -> List a
|
||||
// swap : I64, I64, List a -> List a
|
||||
// swap = \i, j, list ->
|
||||
// when Pair (List.get list i) (List.get list j) is
|
||||
// Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -1316,7 +1316,7 @@ mod gen_list {
|
|||
//
|
||||
// _ ->
|
||||
// []
|
||||
// partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
// partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
// partition = \low, high, initialList ->
|
||||
// when List.get initialList high is
|
||||
// Ok pivot ->
|
||||
|
@ -1328,7 +1328,7 @@ mod gen_list {
|
|||
// Pair (low - 1) initialList
|
||||
//
|
||||
//
|
||||
// partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
|
||||
// partitionHelp : I64, I64, List (Num a), I64, I64 -> [ Pair I64 (List (Num a)) ]
|
||||
//
|
||||
// # when partition 0 0 [ 1,2,3,4,5 ] is
|
||||
// # Pair list _ -> list
|
||||
|
@ -1352,7 +1352,7 @@ mod gen_list {
|
|||
quicksortHelp list 0 (n - 1)
|
||||
|
||||
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
|
@ -1364,7 +1364,7 @@ mod gen_list {
|
|||
list
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -1375,7 +1375,7 @@ mod gen_list {
|
|||
_ ->
|
||||
[]
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -1387,7 +1387,7 @@ mod gen_list {
|
|||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
@ -1422,7 +1422,7 @@ mod gen_list {
|
|||
quicksortHelp list 0 (List.len list - 1)
|
||||
|
||||
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
|
@ -1434,7 +1434,7 @@ mod gen_list {
|
|||
list
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -1445,7 +1445,7 @@ mod gen_list {
|
|||
_ ->
|
||||
[]
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -1457,7 +1457,7 @@ mod gen_list {
|
|||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
# if j < high then
|
||||
if False then
|
||||
|
@ -1495,7 +1495,7 @@ mod gen_list {
|
|||
quicksortHelp list 0 (List.len list - 1)
|
||||
|
||||
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
|
@ -1507,7 +1507,7 @@ mod gen_list {
|
|||
list
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -1518,7 +1518,7 @@ mod gen_list {
|
|||
_ ->
|
||||
[]
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -1530,7 +1530,7 @@ mod gen_list {
|
|||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
@ -1562,7 +1562,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : List Int
|
||||
x : List I64
|
||||
x = []
|
||||
|
||||
List.len x + List.len x
|
||||
|
@ -1578,7 +1578,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : List Int
|
||||
x : List I64
|
||||
x = [1,2,3]
|
||||
|
||||
List.len x + List.len x
|
||||
|
@ -1594,10 +1594,10 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : List Int
|
||||
x : List I64
|
||||
x = [1,2,3]
|
||||
|
||||
id : List Int -> List Int
|
||||
id : List I64 -> List I64
|
||||
id = \y -> y
|
||||
|
||||
id x
|
||||
|
@ -1613,10 +1613,10 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : List Int
|
||||
x : List I64
|
||||
x = [1,2,3]
|
||||
|
||||
id : List Int -> List Int
|
||||
id : List I64 -> List I64
|
||||
id = \y -> List.set y 0 0
|
||||
|
||||
id x
|
||||
|
@ -1632,7 +1632,7 @@ mod gen_list {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
id : List Int -> [ Pair (List Int) Int ]
|
||||
id : List I64 -> [ Pair (List I64) I64 ]
|
||||
id = \y -> Pair y 4
|
||||
|
||||
when id [1,2,3] is
|
||||
|
|
|
@ -135,7 +135,7 @@ mod gen_primitives {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [ Pair Int Int ]
|
||||
x : [ Pair I64 I64 ]
|
||||
x = Pair 0x2 0x3
|
||||
|
||||
when x is
|
||||
|
@ -152,7 +152,7 @@ mod gen_primitives {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [A Int, B Int]
|
||||
x : [A I64, B I64]
|
||||
x = A 0x2
|
||||
|
||||
when x is
|
||||
|
@ -170,7 +170,7 @@ mod gen_primitives {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [A Int, B Int]
|
||||
x : [A I64, B I64]
|
||||
x = B 0x3
|
||||
|
||||
when x is
|
||||
|
@ -293,7 +293,7 @@ mod gen_primitives {
|
|||
indoc!(
|
||||
r#"
|
||||
wrapper = \{} ->
|
||||
alwaysFloatIdentity : Int -> (F64 -> F64)
|
||||
alwaysFloatIdentity : I64 -> (F64 -> F64)
|
||||
alwaysFloatIdentity = \_ ->
|
||||
(\a -> a)
|
||||
|
||||
|
@ -557,14 +557,14 @@ mod gen_primitives {
|
|||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
len : LinkedList a -> Int
|
||||
len : LinkedList a -> I64
|
||||
len = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
Cons _ rest -> 1 + len rest
|
||||
|
||||
main =
|
||||
nil : LinkedList Int
|
||||
nil : LinkedList I64
|
||||
nil = Nil
|
||||
|
||||
len nil
|
||||
|
@ -584,10 +584,10 @@ mod gen_primitives {
|
|||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
nil : LinkedList Int
|
||||
nil : LinkedList I64
|
||||
nil = Nil
|
||||
|
||||
length : LinkedList a -> Int
|
||||
length : LinkedList a -> I64
|
||||
length = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
|
@ -611,10 +611,10 @@ mod gen_primitives {
|
|||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
one : LinkedList Int
|
||||
one : LinkedList I64
|
||||
one = Cons 1 Nil
|
||||
|
||||
length : LinkedList a -> Int
|
||||
length : LinkedList a -> I64
|
||||
length = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
|
@ -638,10 +638,10 @@ mod gen_primitives {
|
|||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
one : LinkedList Int
|
||||
one : LinkedList I64
|
||||
one = Cons 1 Nil
|
||||
|
||||
length : LinkedList a -> Int
|
||||
length : LinkedList a -> I64
|
||||
length = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
|
@ -665,10 +665,10 @@ mod gen_primitives {
|
|||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
three : LinkedList Int
|
||||
three : LinkedList I64
|
||||
three = Cons 3 (Cons 2 (Cons 1 Nil))
|
||||
|
||||
length : LinkedList a -> Int
|
||||
length : LinkedList a -> I64
|
||||
length = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
|
@ -693,7 +693,7 @@ mod gen_primitives {
|
|||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
three : LinkedList Int
|
||||
three : LinkedList I64
|
||||
three = Cons 3 (Cons 2 (Cons 1 Nil))
|
||||
|
||||
|
||||
|
@ -721,10 +721,10 @@ mod gen_primitives {
|
|||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
zero : LinkedList Int
|
||||
zero : LinkedList I64
|
||||
zero = Nil
|
||||
|
||||
sum : LinkedList Int -> Int
|
||||
sum : LinkedList I64 -> I64
|
||||
sum = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
|
@ -748,7 +748,7 @@ mod gen_primitives {
|
|||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
three : LinkedList Int
|
||||
three : LinkedList I64
|
||||
three = Cons 3 (Cons 2 (Cons 1 Nil))
|
||||
|
||||
sum : LinkedList (Num a) -> Num a
|
||||
|
@ -779,7 +779,7 @@ mod gen_primitives {
|
|||
r#"
|
||||
Maybe a : [ Nothing, Just a ]
|
||||
|
||||
x : Maybe (Maybe Int)
|
||||
x : Maybe (Maybe I64)
|
||||
x = Just (Just 41)
|
||||
|
||||
when x is
|
||||
|
@ -796,7 +796,7 @@ mod gen_primitives {
|
|||
r#"
|
||||
Maybe a : [ Nothing, Just a ]
|
||||
|
||||
x : Maybe (Maybe Int)
|
||||
x : Maybe (Maybe I64)
|
||||
x = Just Nothing
|
||||
|
||||
when x is
|
||||
|
@ -814,7 +814,7 @@ mod gen_primitives {
|
|||
r#"
|
||||
Maybe a : [ Nothing, Just a ]
|
||||
|
||||
x : Maybe (Maybe Int)
|
||||
x : Maybe (Maybe I64)
|
||||
x = Nothing
|
||||
|
||||
when x is
|
||||
|
@ -1128,7 +1128,7 @@ mod gen_primitives {
|
|||
|
||||
main : Bool
|
||||
main =
|
||||
myList : ConsList Int
|
||||
myList : ConsList I64
|
||||
myList = empty
|
||||
|
||||
isEmpty myList
|
||||
|
@ -1159,7 +1159,7 @@ mod gen_primitives {
|
|||
|
||||
main : Bool
|
||||
main =
|
||||
myList : ConsList Int
|
||||
myList : ConsList I64
|
||||
myList = Cons 0x1 Nil
|
||||
|
||||
isEmpty myList
|
||||
|
@ -1177,16 +1177,16 @@ mod gen_primitives {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
State a : { count : Int, x : a }
|
||||
State a : { count : I64, x : a }
|
||||
|
||||
foo : State a -> Int
|
||||
foo : State a -> I64
|
||||
foo = \state ->
|
||||
if state.count == 0 then
|
||||
0
|
||||
else
|
||||
1 + foo { count: state.count - 1, x: state.x }
|
||||
|
||||
main : Int
|
||||
main : I64
|
||||
main =
|
||||
foo { count: 3, x: {} }
|
||||
"#
|
||||
|
@ -1267,7 +1267,7 @@ mod gen_primitives {
|
|||
_ ->
|
||||
Node color key value left right
|
||||
|
||||
main : Dict Int {}
|
||||
main : Dict I64 {}
|
||||
main =
|
||||
insert 0 {} Empty
|
||||
"#
|
||||
|
@ -1308,7 +1308,7 @@ mod gen_primitives {
|
|||
_ ->
|
||||
Empty
|
||||
|
||||
main : Dict Int
|
||||
main : Dict I64
|
||||
main =
|
||||
balance Red 0 Empty Empty
|
||||
"#
|
||||
|
@ -1331,7 +1331,7 @@ mod gen_primitives {
|
|||
balance = \key, left ->
|
||||
Node key left Empty
|
||||
|
||||
main : Dict Int
|
||||
main : Dict I64
|
||||
main =
|
||||
balance 0 Empty
|
||||
"#
|
||||
|
@ -1378,7 +1378,7 @@ mod gen_primitives {
|
|||
_ ->
|
||||
Empty
|
||||
|
||||
main : Dict Int Int
|
||||
main : Dict I64 I64
|
||||
main =
|
||||
balance Red 0 0 Empty Empty
|
||||
"#
|
||||
|
@ -1428,7 +1428,7 @@ mod gen_primitives {
|
|||
_ ->
|
||||
Node color key value left right
|
||||
|
||||
main : Dict Int Int
|
||||
main : Dict I64 I64
|
||||
main =
|
||||
balance Red 0 0 Empty Empty
|
||||
"#
|
||||
|
@ -1448,7 +1448,7 @@ mod gen_primitives {
|
|||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
balance : ConsList Int -> Int
|
||||
balance : ConsList I64 -> I64
|
||||
balance = \right ->
|
||||
when right is
|
||||
Cons 1 foo ->
|
||||
|
@ -1457,7 +1457,7 @@ mod gen_primitives {
|
|||
_ -> 3
|
||||
_ -> 3
|
||||
|
||||
main : Int
|
||||
main : I64
|
||||
main =
|
||||
when balance Nil is
|
||||
_ -> 3
|
||||
|
@ -1474,13 +1474,13 @@ mod gen_primitives {
|
|||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
balance : ConsList Int -> Int
|
||||
balance : ConsList I64 -> I64
|
||||
balance = \right ->
|
||||
when right is
|
||||
Cons 1 (Cons 1 _) -> 3
|
||||
_ -> 3
|
||||
|
||||
main : Int
|
||||
main : I64
|
||||
main =
|
||||
when balance Nil is
|
||||
_ -> 3
|
||||
|
@ -1502,7 +1502,7 @@ mod gen_primitives {
|
|||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
balance : ConsList Int -> Int
|
||||
balance : ConsList I64 -> I64
|
||||
balance = \right ->
|
||||
when right is
|
||||
Cons 1 foo ->
|
||||
|
@ -1511,7 +1511,7 @@ mod gen_primitives {
|
|||
_ -> 3
|
||||
_ -> 3
|
||||
|
||||
main : Int
|
||||
main : I64
|
||||
main =
|
||||
when balance Nil is
|
||||
_ -> 3
|
||||
|
@ -1531,13 +1531,13 @@ mod gen_primitives {
|
|||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
foo : ConsList Int -> Int
|
||||
foo : ConsList I64 -> I64
|
||||
foo = \list ->
|
||||
when list is
|
||||
Cons _ (Cons x _) -> x
|
||||
_ -> 0
|
||||
|
||||
main : Int
|
||||
main : I64
|
||||
main =
|
||||
foo (Cons 1 (Cons 32 Nil))
|
||||
"#
|
||||
|
@ -1554,15 +1554,15 @@ mod gen_primitives {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
BTree : [ Node BTree BTree, Leaf Int ]
|
||||
BTree : [ Node BTree BTree, Leaf I64 ]
|
||||
|
||||
foo : BTree -> Int
|
||||
foo : BTree -> I64
|
||||
foo = \btree ->
|
||||
when btree is
|
||||
Node (Node (Leaf x) _) _ -> x
|
||||
_ -> 0
|
||||
|
||||
main : Int
|
||||
main : I64
|
||||
main =
|
||||
foo (Node (Node (Leaf 32) (Leaf 0)) (Leaf 0))
|
||||
"#
|
||||
|
|
|
@ -20,7 +20,7 @@ mod gen_tags {
|
|||
r#"
|
||||
Maybe a : [ Just a, Nothing ]
|
||||
|
||||
x : Maybe Int
|
||||
x : Maybe I64
|
||||
x = Nothing
|
||||
|
||||
x
|
||||
|
@ -39,7 +39,7 @@ mod gen_tags {
|
|||
r#"
|
||||
Maybe a : [ Just a, Nothing ]
|
||||
|
||||
x : Maybe Int
|
||||
x : Maybe I64
|
||||
x = Nothing
|
||||
|
||||
x
|
||||
|
@ -58,7 +58,7 @@ mod gen_tags {
|
|||
r#"
|
||||
Maybe a : [ Just a, Nothing ]
|
||||
|
||||
y : Maybe Int
|
||||
y : Maybe I64
|
||||
y = Just 0x4
|
||||
|
||||
y
|
||||
|
@ -76,7 +76,7 @@ mod gen_tags {
|
|||
r#"
|
||||
Maybe a : [ Just a, Nothing ]
|
||||
|
||||
y : Maybe Int
|
||||
y : Maybe I64
|
||||
y = Just 0x4
|
||||
|
||||
y
|
||||
|
@ -114,7 +114,7 @@ mod gen_tags {
|
|||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// x : Result Int Int
|
||||
// x : Result I64 I64
|
||||
// x = Err 41
|
||||
|
||||
// x
|
||||
|
@ -185,7 +185,7 @@ mod gen_tags {
|
|||
// r#"
|
||||
// LinkedList a : [ Cons a (LinkedList a), Nil ]
|
||||
//
|
||||
// empty : LinkedList Int
|
||||
// empty : LinkedList I64
|
||||
// empty = Nil
|
||||
//
|
||||
// 1
|
||||
|
@ -203,7 +203,7 @@ mod gen_tags {
|
|||
// r#"
|
||||
// LinkedList a : [ Cons a (LinkedList a), Nil ]
|
||||
//
|
||||
// singleton : LinkedList Int
|
||||
// singleton : LinkedList I64
|
||||
// singleton = Cons 0x1 Nil
|
||||
//
|
||||
// 1
|
||||
|
@ -290,7 +290,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [ Nothing, Just Int ]
|
||||
x : [ Nothing, Just I64 ]
|
||||
x = Nothing
|
||||
|
||||
when x is
|
||||
|
@ -308,7 +308,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [ Nothing, Just Int ]
|
||||
x : [ Nothing, Just I64 ]
|
||||
x = Just 41
|
||||
|
||||
when x is
|
||||
|
@ -326,7 +326,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Result Int Int
|
||||
x : Result I64 I64
|
||||
x = Err 41
|
||||
|
||||
when x is
|
||||
|
@ -346,7 +346,7 @@ mod gen_tags {
|
|||
r#"
|
||||
These a b : [ This a, That b, These a b ]
|
||||
|
||||
x : These Int Int
|
||||
x : These I64 I64
|
||||
x = These 0x3 0x2
|
||||
|
||||
when x is
|
||||
|
@ -398,7 +398,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Result Int Int
|
||||
x : Result I64 I64
|
||||
x = Ok 2
|
||||
|
||||
when x is
|
||||
|
@ -464,7 +464,7 @@ mod gen_tags {
|
|||
r#"
|
||||
Maybe a : [ Nothing, Just a ]
|
||||
|
||||
x : Maybe (Maybe Int)
|
||||
x : Maybe (Maybe I64)
|
||||
x = Just (Just 41)
|
||||
|
||||
when x is
|
||||
|
@ -558,7 +558,7 @@ mod gen_tags {
|
|||
r#"
|
||||
Unit : [ Unit ]
|
||||
|
||||
f : Unit -> Int
|
||||
f : Unit -> I64
|
||||
f = \Unit -> 42
|
||||
|
||||
f Unit
|
||||
|
@ -587,7 +587,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
f : {} -> Int
|
||||
f : {} -> I64
|
||||
f = \{} -> 42
|
||||
|
||||
f {}
|
||||
|
@ -614,7 +614,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [ Pair Int ]
|
||||
x : [ Pair I64 ]
|
||||
x = Pair 2
|
||||
|
||||
x
|
||||
|
@ -634,7 +634,7 @@ mod gen_tags {
|
|||
|
||||
Maybe a : [ Nothing, Just a ]
|
||||
|
||||
x : Maybe (Maybe Int)
|
||||
x : Maybe (Maybe I64)
|
||||
x = Just (Just 41)
|
||||
|
||||
main =
|
||||
|
@ -806,7 +806,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r"#
|
||||
x : [ Three Bool Int, Empty ]
|
||||
x : [ Three Bool I64, Empty ]
|
||||
x = Three (1 == 1) 32
|
||||
|
||||
x
|
||||
|
@ -820,7 +820,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r"#
|
||||
x : [ Three Bool [ Red, Green, Blue ] Int, Empty ]
|
||||
x : [ Three Bool [ Red, Green, Blue ] I64, Empty ]
|
||||
x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32
|
||||
|
||||
x
|
||||
|
@ -836,7 +836,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r"#
|
||||
x : [ Three Bool Int, Empty ]
|
||||
x : [ Three Bool I64, Empty ]
|
||||
x = Three (1 == 1) 32
|
||||
|
||||
when x is
|
||||
|
@ -854,7 +854,7 @@ mod gen_tags {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r"#
|
||||
x : [ Three Bool [ Red, Green, Blue ] Int, Empty ]
|
||||
x : [ Three Bool [ Red, Green, Blue ] I64, Empty ]
|
||||
x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32
|
||||
|
||||
when x is
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
|
||||
#[macro_use]
|
||||
extern crate indoc;
|
||||
|
||||
|
|
|
@ -75,7 +75,6 @@ pub fn helper<'a>(
|
|||
println!("{:?}", exposed_to_host);
|
||||
println!("=================================\n");
|
||||
*/
|
||||
|
||||
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||
let main_fn_symbol = exposed_to_host.keys().copied().nth(0).unwrap();
|
||||
|
||||
|
|
|
@ -13,14 +13,18 @@ use roc_constrain::module::{
|
|||
constrain_imports, pre_constrain_imports, ConstrainableImports, Import,
|
||||
};
|
||||
use roc_constrain::module::{constrain_module, ExposedModuleTypes, SubsByModule};
|
||||
use roc_module::ident::{Ident, Lowercase, ModuleName, TagName};
|
||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
|
||||
use roc_module::ident::{Ident, Lowercase, ModuleName, QualifiedModuleName, TagName};
|
||||
use roc_module::symbol::{
|
||||
IdentIds, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, Symbol,
|
||||
};
|
||||
use roc_mono::ir::{
|
||||
CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs,
|
||||
};
|
||||
use roc_mono::layout::{Layout, LayoutCache};
|
||||
use roc_parse::ast::{self, Attempting, StrLiteral, TypeAnnotation};
|
||||
use roc_parse::header::{ExposesEntry, ImportsEntry, PlatformHeader, TypedIdent};
|
||||
use roc_parse::header::{
|
||||
ExposesEntry, ImportsEntry, PackageEntry, PackageOrPath, PlatformHeader, To, TypedIdent,
|
||||
};
|
||||
use roc_parse::module::module_defs;
|
||||
use roc_parse::parser::{self, Fail, Parser};
|
||||
use roc_region::all::{Located, Region};
|
||||
|
@ -101,6 +105,7 @@ impl Dependencies {
|
|||
pub fn add_module(
|
||||
&mut self,
|
||||
module_id: ModuleId,
|
||||
opt_effect_module: Option<ModuleId>,
|
||||
dependencies: &MutSet<ModuleId>,
|
||||
goal_phase: Phase,
|
||||
) -> MutSet<(ModuleId, Phase)> {
|
||||
|
@ -143,7 +148,8 @@ impl Dependencies {
|
|||
// all the dependencies can be loaded
|
||||
for dep in dependencies {
|
||||
// TODO figure out how to "load" (because it doesn't exist on the file system) the Effect module
|
||||
if !format!("{:?}", dep).contains("Effect") {
|
||||
|
||||
if Some(*dep) != opt_effect_module {
|
||||
output.insert((*dep, LoadHeader));
|
||||
}
|
||||
}
|
||||
|
@ -226,7 +232,13 @@ impl Dependencies {
|
|||
self.add_dependency_help(a, b, phase, phase);
|
||||
}
|
||||
|
||||
/// phase_a of module a is waiting for phase_b of module_b
|
||||
fn add_dependency_help(&mut self, a: ModuleId, b: ModuleId, phase_a: Phase, phase_b: Phase) {
|
||||
// no need to wait if the dependency is already done!
|
||||
if let Some(Status::Done) = self.status.get(&(b, phase_b)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let key = (a, phase_a);
|
||||
let value = (b, phase_b);
|
||||
match self.waiting_for.get_mut(&key) {
|
||||
|
@ -275,7 +287,7 @@ impl Dependencies {
|
|||
/// Struct storing various intermediate stages by their ModuleId
|
||||
#[derive(Debug, Default)]
|
||||
struct ModuleCache<'a> {
|
||||
module_names: MutMap<ModuleId, ModuleName>,
|
||||
module_names: MutMap<ModuleId, PQModuleName<'a>>,
|
||||
|
||||
/// Phases
|
||||
headers: MutMap<ModuleId, ModuleHeader<'a>>,
|
||||
|
@ -338,14 +350,16 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
|
|||
let dep_name = state
|
||||
.module_cache
|
||||
.module_names
|
||||
.remove(&module_id)
|
||||
.expect("module id is present");
|
||||
.get(&module_id)
|
||||
.expect("module id is present")
|
||||
.clone();
|
||||
|
||||
BuildTask::LoadModule {
|
||||
module_name: dep_name,
|
||||
// Provide mutexes of ModuleIds and IdentIds by module,
|
||||
// so other modules can populate them as they load.
|
||||
module_ids: Arc::clone(&state.arc_modules),
|
||||
shorthands: Arc::clone(&state.arc_shorthands),
|
||||
ident_ids_by_module: Arc::clone(&state.ident_ids_by_module),
|
||||
mode: state.stdlib.mode,
|
||||
}
|
||||
|
@ -394,8 +408,10 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
|
|||
// This should be small, and cloning it should be quick.
|
||||
// We release the lock as soon as we're done cloning, so we don't have
|
||||
// to lock the global module_ids while canonicalizing any given module.
|
||||
let module_ids = Arc::clone(&state.arc_modules);
|
||||
let module_ids = { (*module_ids).lock().clone() };
|
||||
let qualified_module_ids = Arc::clone(&state.arc_modules);
|
||||
let qualified_module_ids = { (*qualified_module_ids).lock().clone() };
|
||||
|
||||
let module_ids = qualified_module_ids.into_module_ids();
|
||||
|
||||
let exposed_symbols = state
|
||||
.exposed_symbols_by_module
|
||||
|
@ -407,9 +423,8 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
|
|||
for imported in parsed.imported_modules.iter() {
|
||||
match state.module_cache.aliases.get(imported) {
|
||||
None => unreachable!(
|
||||
"imported module {:?} did not register its aliases, so {:?} cannot use them",
|
||||
imported,
|
||||
parsed.module_id,
|
||||
r"imported module {:?} did not register its aliases, so {:?} cannot use them",
|
||||
imported, parsed.module_id,
|
||||
),
|
||||
Some(new) => {
|
||||
// TODO filter to only add imported aliases
|
||||
|
@ -539,10 +554,12 @@ struct ModuleHeader<'a> {
|
|||
module_name: AppOrInterfaceName<'a>,
|
||||
module_path: PathBuf,
|
||||
exposed_ident_ids: IdentIds,
|
||||
deps_by_name: MutMap<ModuleName, ModuleId>,
|
||||
deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
|
||||
packages: MutMap<&'a str, PackageOrPath<'a>>,
|
||||
imported_modules: MutSet<ModuleId>,
|
||||
exposes: Vec<Symbol>,
|
||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
to_platform: Option<To<'a>>,
|
||||
src: &'a [u8],
|
||||
module_timing: ModuleTiming,
|
||||
}
|
||||
|
@ -584,6 +601,7 @@ pub struct MonomorphizedModule<'a> {
|
|||
pub interns: Interns,
|
||||
pub subs: Subs,
|
||||
pub output_path: Box<str>,
|
||||
pub platform_path: Box<str>,
|
||||
pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||
pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
||||
pub mono_problems: MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
||||
|
@ -606,7 +624,7 @@ struct ParsedModule<'a> {
|
|||
module_path: PathBuf,
|
||||
src: &'a str,
|
||||
module_timing: ModuleTiming,
|
||||
deps_by_name: MutMap<ModuleName, ModuleId>,
|
||||
deps_by_name: MutMap<PQModuleName<'a>, ModuleId>,
|
||||
imported_modules: MutSet<ModuleId>,
|
||||
exposed_ident_ids: IdentIds,
|
||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||
|
@ -676,6 +694,8 @@ struct State<'a> {
|
|||
pub stdlib: StdLib,
|
||||
pub exposed_types: SubsByModule,
|
||||
pub output_path: Option<&'a str>,
|
||||
pub platform_path: Option<To<'a>>,
|
||||
pub opt_effect_module: Option<ModuleId>,
|
||||
|
||||
pub headers_parsed: MutSet<ModuleId>,
|
||||
|
||||
|
@ -689,7 +709,8 @@ struct State<'a> {
|
|||
pub constrained_ident_ids: MutMap<ModuleId, IdentIds>,
|
||||
|
||||
/// From now on, these will be used by multiple threads; time to make an Arc<Mutex<_>>!
|
||||
pub arc_modules: Arc<Mutex<ModuleIds>>,
|
||||
pub arc_modules: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
pub arc_shorthands: Arc<Mutex<MutMap<&'a str, PackageOrPath<'a>>>>,
|
||||
|
||||
pub ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
|
||||
|
@ -817,13 +838,15 @@ impl ModuleTiming {
|
|||
#[allow(dead_code)]
|
||||
enum BuildTask<'a> {
|
||||
LoadModule {
|
||||
module_name: ModuleName,
|
||||
module_ids: Arc<Mutex<ModuleIds>>,
|
||||
module_name: PQModuleName<'a>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
shorthands: Arc<Mutex<MutMap<&'a str, PackageOrPath<'a>>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
mode: Mode,
|
||||
},
|
||||
LoadPkgConfig {
|
||||
module_ids: Arc<Mutex<ModuleIds>>,
|
||||
shorthand: &'a str,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
mode: Mode,
|
||||
},
|
||||
|
@ -877,6 +900,7 @@ pub enum LoadingProblem {
|
|||
FileProblem {
|
||||
filename: PathBuf,
|
||||
error: io::ErrorKind,
|
||||
msg: &'static str,
|
||||
},
|
||||
ParsingFailed {
|
||||
filename: PathBuf,
|
||||
|
@ -988,7 +1012,7 @@ pub fn load_and_monomorphize_from_str<'a>(
|
|||
}
|
||||
|
||||
struct LoadStart<'a> {
|
||||
pub arc_modules: Arc<Mutex<ModuleIds>>,
|
||||
pub arc_modules: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
pub ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
pub root_id: ModuleId,
|
||||
pub root_msg: Msg<'a>,
|
||||
|
@ -1000,7 +1024,7 @@ impl<'a> LoadStart<'a> {
|
|||
filename: PathBuf,
|
||||
mode: Mode,
|
||||
) -> Result<Self, LoadingProblem> {
|
||||
let arc_modules = Arc::new(Mutex::new(ModuleIds::default()));
|
||||
let arc_modules = Arc::new(Mutex::new(PackageModuleIds::default()));
|
||||
let root_exposed_ident_ids = IdentIds::exposed_builtins(0);
|
||||
let ident_ids_by_module = Arc::new(Mutex::new(root_exposed_ident_ids));
|
||||
|
||||
|
@ -1011,6 +1035,7 @@ impl<'a> LoadStart<'a> {
|
|||
load_filename(
|
||||
arena,
|
||||
filename,
|
||||
None,
|
||||
Arc::clone(&arc_modules),
|
||||
Arc::clone(&ident_ids_by_module),
|
||||
root_start_time,
|
||||
|
@ -1032,7 +1057,7 @@ impl<'a> LoadStart<'a> {
|
|||
src: &'a str,
|
||||
mode: Mode,
|
||||
) -> Result<Self, LoadingProblem> {
|
||||
let arc_modules = Arc::new(Mutex::new(ModuleIds::default()));
|
||||
let arc_modules = Arc::new(Mutex::new(PackageModuleIds::default()));
|
||||
let root_exposed_ident_ids = IdentIds::exposed_builtins(0);
|
||||
let ident_ids_by_module = Arc::new(Mutex::new(root_exposed_ident_ids));
|
||||
|
||||
|
@ -1126,6 +1151,8 @@ where
|
|||
root_msg,
|
||||
} = load_start;
|
||||
|
||||
let arc_shorthands = Arc::new(Mutex::new(MutMap::default()));
|
||||
|
||||
let (msg_tx, msg_rx) = bounded(1024);
|
||||
msg_tx
|
||||
.send(root_msg)
|
||||
|
@ -1248,6 +1275,8 @@ where
|
|||
goal_phase,
|
||||
stdlib,
|
||||
output_path: None,
|
||||
platform_path: None,
|
||||
opt_effect_module: None,
|
||||
module_cache: ModuleCache::default(),
|
||||
dependencies: Dependencies::default(),
|
||||
procedures: MutMap::default(),
|
||||
|
@ -1256,6 +1285,7 @@ where
|
|||
headers_parsed,
|
||||
loading_started,
|
||||
arc_modules,
|
||||
arc_shorthands,
|
||||
constrained_ident_ids: IdentIds::exposed_builtins(0),
|
||||
ident_ids_by_module,
|
||||
declarations_by_id: MutMap::default(),
|
||||
|
@ -1388,6 +1418,16 @@ fn update<'a>(
|
|||
log!("loaded header for {:?}", header.module_id);
|
||||
let home = header.module_id;
|
||||
|
||||
{
|
||||
let mut shorthands = (*state.arc_shorthands).lock();
|
||||
|
||||
for (shorthand, package_or_path) in header.packages.iter() {
|
||||
shorthands.insert(shorthand, package_or_path.clone());
|
||||
}
|
||||
}
|
||||
|
||||
state.platform_path = state.platform_path.or_else(|| header.to_platform.clone());
|
||||
|
||||
// store an ID to name mapping, so we know the file to read when fetching dependencies' headers
|
||||
for (name, id) in header.deps_by_name.iter() {
|
||||
state.module_cache.module_names.insert(*id, name.clone());
|
||||
|
@ -1412,6 +1452,7 @@ fn update<'a>(
|
|||
|
||||
let work = state.dependencies.add_module(
|
||||
header.module_id,
|
||||
state.opt_effect_module,
|
||||
&header.imported_modules,
|
||||
state.goal_phase,
|
||||
);
|
||||
|
@ -1499,6 +1540,9 @@ fn update<'a>(
|
|||
module_docs,
|
||||
} => {
|
||||
let module_id = constrained_module.module.module_id;
|
||||
|
||||
state.opt_effect_module = Some(module_id);
|
||||
|
||||
log!("made effect module for {:?}", module_id);
|
||||
state
|
||||
.module_cache
|
||||
|
@ -1761,7 +1805,8 @@ fn finish_specialization<'a>(
|
|||
) -> MonomorphizedModule<'a> {
|
||||
let module_ids = Arc::try_unwrap(state.arc_modules)
|
||||
.unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids"))
|
||||
.into_inner();
|
||||
.into_inner()
|
||||
.into_module_ids();
|
||||
|
||||
let interns = Interns {
|
||||
module_ids,
|
||||
|
@ -1772,6 +1817,7 @@ fn finish_specialization<'a>(
|
|||
procedures,
|
||||
module_cache,
|
||||
output_path,
|
||||
platform_path,
|
||||
..
|
||||
} = state;
|
||||
|
||||
|
@ -1788,11 +1834,33 @@ fn finish_specialization<'a>(
|
|||
.map(|(id, (path, src))| (id, (path, src.into())))
|
||||
.collect();
|
||||
|
||||
let path_to_platform = {
|
||||
let package_or_path = match platform_path {
|
||||
Some(To::ExistingPackage(shorthand)) => {
|
||||
match (*state.arc_shorthands).lock().get(shorthand) {
|
||||
Some(p_or_p) => p_or_p.clone(),
|
||||
None => unreachable!(),
|
||||
}
|
||||
}
|
||||
Some(To::NewPackage(p_or_p)) => p_or_p,
|
||||
None => panic!("no platform!"),
|
||||
};
|
||||
|
||||
match package_or_path {
|
||||
PackageOrPath::Path(StrLiteral::PlainLine(path)) => path,
|
||||
PackageOrPath::Path(_) => unreachable!("invalid"),
|
||||
_ => todo!("packages"),
|
||||
}
|
||||
};
|
||||
|
||||
let platform_path = path_to_platform.into();
|
||||
|
||||
MonomorphizedModule {
|
||||
can_problems,
|
||||
mono_problems,
|
||||
type_problems,
|
||||
output_path: output_path.unwrap_or(DEFAULT_APP_OUTPUT_PATH).into(),
|
||||
platform_path,
|
||||
exposed_to_host,
|
||||
module_id: state.root_id,
|
||||
subs,
|
||||
|
@ -1811,7 +1879,8 @@ fn finish<'a>(
|
|||
) -> LoadedModule {
|
||||
let module_ids = Arc::try_unwrap(state.arc_modules)
|
||||
.unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids"))
|
||||
.into_inner();
|
||||
.into_inner()
|
||||
.into_module_ids();
|
||||
|
||||
let interns = Interns {
|
||||
module_ids,
|
||||
|
@ -1843,19 +1912,14 @@ fn finish<'a>(
|
|||
fn load_pkg_config<'a>(
|
||||
arena: &'a Bump,
|
||||
src_dir: &Path,
|
||||
module_ids: Arc<Mutex<ModuleIds>>,
|
||||
shorthand: &'a str,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
mode: Mode,
|
||||
) -> Result<Msg<'a>, LoadingProblem> {
|
||||
let module_start_time = SystemTime::now();
|
||||
|
||||
let mut filename = PathBuf::from(src_dir);
|
||||
|
||||
filename.push("platform");
|
||||
filename.push(PKG_CONFIG_FILE_NAME);
|
||||
|
||||
// End with .roc
|
||||
filename.set_extension(ROC_FILE_EXTENSION);
|
||||
let filename = PathBuf::from(src_dir);
|
||||
|
||||
let file_io_start = SystemTime::now();
|
||||
let file = fs::read(&filename);
|
||||
|
@ -1889,6 +1953,7 @@ fn load_pkg_config<'a>(
|
|||
}
|
||||
Ok((ast::Module::Platform { header }, _parse_state)) => fabricate_effects_module(
|
||||
arena,
|
||||
shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
|
@ -1903,6 +1968,7 @@ fn load_pkg_config<'a>(
|
|||
Err(err) => Err(LoadingProblem::FileProblem {
|
||||
filename,
|
||||
error: err.kind(),
|
||||
msg: "while reading a Pkg-Config.roc file",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -1911,8 +1977,9 @@ fn load_pkg_config<'a>(
|
|||
fn load_module<'a>(
|
||||
arena: &'a Bump,
|
||||
src_dir: &Path,
|
||||
module_name: ModuleName,
|
||||
module_ids: Arc<Mutex<ModuleIds>>,
|
||||
module_name: PQModuleName<'a>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
arc_shorthands: Arc<Mutex<MutMap<&'a str, PackageOrPath<'a>>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
mode: Mode,
|
||||
) -> Result<(ModuleId, Msg<'a>), LoadingProblem> {
|
||||
|
@ -1921,10 +1988,36 @@ fn load_module<'a>(
|
|||
|
||||
filename.push(src_dir);
|
||||
|
||||
let opt_shorthand;
|
||||
match module_name {
|
||||
PQModuleName::Unqualified(name) => {
|
||||
opt_shorthand = None;
|
||||
// Convert dots in module name to directories
|
||||
for part in module_name.as_str().split(MODULE_SEPARATOR) {
|
||||
for part in name.split(MODULE_SEPARATOR) {
|
||||
filename.push(part);
|
||||
}
|
||||
}
|
||||
PQModuleName::Qualified(shorthand, name) => {
|
||||
opt_shorthand = Some(shorthand);
|
||||
let shorthands = arc_shorthands.lock();
|
||||
|
||||
match shorthands.get(shorthand) {
|
||||
Some(PackageOrPath::Path(StrLiteral::PlainLine(path))) => {
|
||||
filename.push(path);
|
||||
}
|
||||
Some(PackageOrPath::Path(_str_liteal)) => {
|
||||
unreachable!("invalid structure for path")
|
||||
}
|
||||
Some(PackageOrPath::Package(_name, _version)) => todo!("packages"),
|
||||
None => unreachable!("there is no shorthand named {:?}", shorthand),
|
||||
}
|
||||
|
||||
// Convert dots in module name to directories
|
||||
for part in name.split(MODULE_SEPARATOR) {
|
||||
filename.push(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End with .roc
|
||||
filename.set_extension(ROC_FILE_EXTENSION);
|
||||
|
@ -1932,6 +2025,7 @@ fn load_module<'a>(
|
|||
load_filename(
|
||||
arena,
|
||||
filename,
|
||||
opt_shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
module_start_time,
|
||||
|
@ -1970,7 +2064,8 @@ fn parse_header<'a>(
|
|||
arena: &'a Bump,
|
||||
read_file_duration: Duration,
|
||||
filename: PathBuf,
|
||||
module_ids: Arc<Mutex<ModuleIds>>,
|
||||
opt_shorthand: Option<&'a str>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
mode: Mode,
|
||||
src_bytes: &'a [u8],
|
||||
|
@ -1994,8 +2089,11 @@ fn parse_header<'a>(
|
|||
value: AppOrInterfaceName::Interface(header.name.value),
|
||||
},
|
||||
filename,
|
||||
opt_shorthand,
|
||||
&[],
|
||||
header.exposes.into_bump_slice(),
|
||||
header.imports.into_bump_slice(),
|
||||
None,
|
||||
parse_state,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
|
@ -2005,30 +2103,63 @@ fn parse_header<'a>(
|
|||
let mut pkg_config_dir = filename.clone();
|
||||
pkg_config_dir.pop();
|
||||
|
||||
let packages = header.packages.into_bump_slice();
|
||||
|
||||
let (module_id, app_module_header_msg) = send_header(
|
||||
Located {
|
||||
region: header.name.region,
|
||||
value: AppOrInterfaceName::App(header.name.value),
|
||||
},
|
||||
filename,
|
||||
opt_shorthand,
|
||||
packages,
|
||||
header.provides.into_bump_slice(),
|
||||
header.imports.into_bump_slice(),
|
||||
Some(header.to.value.clone()),
|
||||
parse_state,
|
||||
module_ids.clone(),
|
||||
ident_ids_by_module.clone(),
|
||||
module_timing,
|
||||
);
|
||||
|
||||
match header.to.value {
|
||||
To::ExistingPackage(existing_package) => {
|
||||
let opt_base_package = packages.iter().find(|loc_package_entry| {
|
||||
let Located { value, .. } = loc_package_entry;
|
||||
|
||||
match value {
|
||||
PackageEntry::Entry { shorthand, .. } => shorthand == &existing_package,
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
|
||||
match opt_base_package {
|
||||
Some(Located {
|
||||
value:
|
||||
PackageEntry::Entry {
|
||||
shorthand,
|
||||
package_or_path:
|
||||
Located {
|
||||
value: package_or_path,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
}) => {
|
||||
match package_or_path {
|
||||
PackageOrPath::Path(StrLiteral::PlainLine(package)) => {
|
||||
// check whether we can find a Pkg-Config.roc file
|
||||
let mut pkg_config_roc = pkg_config_dir.clone();
|
||||
pkg_config_roc.push("platform");
|
||||
let mut pkg_config_roc = pkg_config_dir;
|
||||
pkg_config_roc.push(package);
|
||||
pkg_config_roc.push(PKG_CONFIG_FILE_NAME);
|
||||
pkg_config_roc.set_extension(ROC_FILE_EXTENSION);
|
||||
|
||||
if pkg_config_roc.as_path().exists() {
|
||||
let load_pkg_config_msg = load_pkg_config(
|
||||
arena,
|
||||
&pkg_config_dir,
|
||||
&pkg_config_roc,
|
||||
shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
|
@ -2036,14 +2167,38 @@ fn parse_header<'a>(
|
|||
|
||||
Ok((
|
||||
module_id,
|
||||
Msg::Many(vec![app_module_header_msg, load_pkg_config_msg]),
|
||||
Msg::Many(vec![
|
||||
app_module_header_msg,
|
||||
load_pkg_config_msg,
|
||||
]),
|
||||
))
|
||||
} else {
|
||||
Ok((module_id, app_module_header_msg))
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => panic!("could not find base"),
|
||||
}
|
||||
}
|
||||
To::NewPackage(package_or_path) => match package_or_path {
|
||||
PackageOrPath::Package(_, _) => panic!("TODO implement packages"),
|
||||
PackageOrPath::Path(StrLiteral::PlainLine(_package)) => {
|
||||
Ok((module_id, app_module_header_msg))
|
||||
}
|
||||
PackageOrPath::Path(StrLiteral::Block(_)) => {
|
||||
panic!("TODO implement block package path")
|
||||
}
|
||||
PackageOrPath::Path(StrLiteral::Line(_)) => {
|
||||
panic!("TODO implement line package path")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok((ast::Module::Platform { header }, _parse_state)) => fabricate_effects_module(
|
||||
arena,
|
||||
&"",
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
|
@ -2058,7 +2213,8 @@ fn parse_header<'a>(
|
|||
fn load_filename<'a>(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
module_ids: Arc<Mutex<ModuleIds>>,
|
||||
opt_shorthand: Option<&'a str>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
module_start_time: SystemTime,
|
||||
mode: Mode,
|
||||
|
@ -2072,6 +2228,7 @@ fn load_filename<'a>(
|
|||
arena,
|
||||
file_io_duration,
|
||||
filename,
|
||||
opt_shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
|
@ -2081,17 +2238,19 @@ fn load_filename<'a>(
|
|||
Err(err) => Err(LoadingProblem::FileProblem {
|
||||
filename,
|
||||
error: err.kind(),
|
||||
msg: "in `load_filename`",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Load a module from a str
|
||||
/// the `filename` is never read, but used for the module name
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn load_from_str<'a>(
|
||||
arena: &'a Bump,
|
||||
filename: PathBuf,
|
||||
src: &'a str,
|
||||
module_ids: Arc<Mutex<ModuleIds>>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
module_start_time: SystemTime,
|
||||
mode: Mode,
|
||||
|
@ -2103,6 +2262,7 @@ fn load_from_str<'a>(
|
|||
arena,
|
||||
file_io_duration,
|
||||
filename,
|
||||
None,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
|
@ -2122,10 +2282,13 @@ enum AppOrInterfaceName<'a> {
|
|||
fn send_header<'a>(
|
||||
loc_name: Located<AppOrInterfaceName<'a>>,
|
||||
filename: PathBuf,
|
||||
opt_shorthand: Option<&'a str>,
|
||||
packages: &'a [Located<PackageEntry<'a>>],
|
||||
exposes: &'a [Located<ExposesEntry<'a, &'a str>>],
|
||||
imports: &'a [Located<ImportsEntry<'a>>],
|
||||
to_platform: Option<To<'a>>,
|
||||
parse_state: parser::State<'a>,
|
||||
module_ids: Arc<Mutex<ModuleIds>>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
module_timing: ModuleTiming,
|
||||
) -> (ModuleId, Msg<'a>) {
|
||||
|
@ -2141,20 +2304,21 @@ fn send_header<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
let mut imported: Vec<(ModuleName, Vec<Ident>, Region)> = Vec::with_capacity(imports.len());
|
||||
let mut imported: Vec<(QualifiedModuleName, Vec<Ident>, Region)> =
|
||||
Vec::with_capacity(imports.len());
|
||||
let mut imported_modules: MutSet<ModuleId> = MutSet::default();
|
||||
let mut scope_size = 0;
|
||||
|
||||
for loc_entry in imports {
|
||||
let (module_name, exposed) = exposed_from_import(&loc_entry.value);
|
||||
let (qualified_module_name, exposed) = exposed_from_import(&loc_entry.value);
|
||||
|
||||
scope_size += exposed.len();
|
||||
|
||||
imported.push((module_name, exposed, loc_entry.region));
|
||||
imported.push((qualified_module_name, exposed, loc_entry.region));
|
||||
}
|
||||
|
||||
let num_exposes = exposes.len();
|
||||
let mut deps_by_name: MutMap<ModuleName, ModuleId> =
|
||||
let mut deps_by_name: MutMap<PQModuleName, ModuleId> =
|
||||
HashMap::with_capacity_and_hasher(num_exposes, default_hasher());
|
||||
let mut exposed: Vec<Symbol> = Vec::with_capacity(num_exposes);
|
||||
|
||||
|
@ -2169,7 +2333,13 @@ fn send_header<'a>(
|
|||
let mut module_ids = (*module_ids).lock();
|
||||
let mut ident_ids_by_module = (*ident_ids_by_module).lock();
|
||||
|
||||
home = module_ids.get_or_insert(&declared_name.as_inline_str());
|
||||
let name = match opt_shorthand {
|
||||
Some(shorthand) => {
|
||||
PQModuleName::Qualified(&shorthand, declared_name.as_inline_str().clone())
|
||||
}
|
||||
None => PQModuleName::Unqualified(declared_name.as_inline_str().clone()),
|
||||
};
|
||||
home = module_ids.get_or_insert(&name);
|
||||
|
||||
// Ensure this module has an entry in the exposed_ident_ids map.
|
||||
ident_ids_by_module
|
||||
|
@ -2178,17 +2348,28 @@ fn send_header<'a>(
|
|||
|
||||
// For each of our imports, add an entry to deps_by_name
|
||||
//
|
||||
// e.g. for `imports [ Foo.{ bar } ]`, add `Foo` to deps_by_name
|
||||
// e.g. for `imports [ base.Foo.{ bar } ]`, add `Foo` to deps_by_name
|
||||
//
|
||||
// Also build a list of imported_values_to_expose (like `bar` above.)
|
||||
for (module_name, exposed_idents, region) in imported.into_iter() {
|
||||
let cloned_module_name = module_name.clone();
|
||||
let module_id = module_ids.get_or_insert(&module_name.into());
|
||||
|
||||
deps_by_name.insert(cloned_module_name, module_id);
|
||||
for (qualified_module_name, exposed_idents, region) in imported.into_iter() {
|
||||
let cloned_module_name = qualified_module_name.module.clone();
|
||||
let pq_module_name = match qualified_module_name.opt_package {
|
||||
None => match opt_shorthand {
|
||||
Some(shorthand) => {
|
||||
PQModuleName::Qualified(shorthand, qualified_module_name.module.into())
|
||||
}
|
||||
None => PQModuleName::Unqualified(qualified_module_name.module.into()),
|
||||
},
|
||||
Some(package) => {
|
||||
PQModuleName::Qualified(package, cloned_module_name.clone().into())
|
||||
}
|
||||
};
|
||||
|
||||
let module_id = module_ids.get_or_insert(&pq_module_name);
|
||||
imported_modules.insert(module_id);
|
||||
|
||||
deps_by_name.insert(pq_module_name, module_id);
|
||||
|
||||
// Add the new exposed idents to the dep module's IdentIds, so
|
||||
// once that module later gets loaded, its lookups will resolve
|
||||
// to the same symbols as the ones we're using here.
|
||||
|
@ -2236,6 +2417,25 @@ fn send_header<'a>(
|
|||
ident_ids.clone()
|
||||
};
|
||||
|
||||
let mut parse_entries: Vec<_> = (&packages).iter().map(|x| &x.value).collect();
|
||||
let mut package_entries = MutMap::default();
|
||||
|
||||
while let Some(parse_entry) = parse_entries.pop() {
|
||||
use PackageEntry::*;
|
||||
match parse_entry {
|
||||
Entry {
|
||||
shorthand,
|
||||
package_or_path,
|
||||
..
|
||||
} => {
|
||||
package_entries.insert(*shorthand, package_or_path.value.clone());
|
||||
}
|
||||
SpaceBefore(inner, _) | SpaceAfter(inner, _) => {
|
||||
parse_entries.push(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send the deps to the coordinator thread for processing,
|
||||
// then continue on to parsing and canonicalizing defs.
|
||||
//
|
||||
|
@ -2249,9 +2449,11 @@ fn send_header<'a>(
|
|||
module_path: filename,
|
||||
exposed_ident_ids: ident_ids,
|
||||
module_name: loc_name.value,
|
||||
packages: package_entries,
|
||||
imported_modules,
|
||||
deps_by_name,
|
||||
exposes: exposed,
|
||||
to_platform,
|
||||
src: parse_state.bytes,
|
||||
exposed_imports: scope,
|
||||
module_timing,
|
||||
|
@ -2370,9 +2572,11 @@ fn run_solve<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn fabricate_effects_module<'a>(
|
||||
arena: &'a Bump,
|
||||
module_ids: Arc<Mutex<ModuleIds>>,
|
||||
shorthand: &'a str,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||
mode: Mode,
|
||||
header: PlatformHeader<'a>,
|
||||
|
@ -2398,12 +2602,23 @@ fn fabricate_effects_module<'a>(
|
|||
functions
|
||||
};
|
||||
|
||||
{
|
||||
let mut module_ids = (*module_ids).lock();
|
||||
|
||||
for exposed in header.exposes {
|
||||
if let ExposesEntry::Exposed(module_name) = exposed.value {
|
||||
module_ids.get_or_insert(&PQModuleName::Qualified(shorthand, module_name.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let exposed_ident_ids = {
|
||||
// Lock just long enough to perform the minimal operations necessary.
|
||||
let mut module_ids = (*module_ids).lock();
|
||||
let mut ident_ids_by_module = (*ident_ids_by_module).lock();
|
||||
|
||||
module_id = module_ids.get_or_insert(&declared_name.as_inline_str());
|
||||
let name = PQModuleName::Qualified(shorthand, declared_name.as_inline_str().clone());
|
||||
module_id = module_ids.get_or_insert(&name);
|
||||
|
||||
// Ensure this module has an entry in the exposed_ident_ids map.
|
||||
ident_ids_by_module
|
||||
|
@ -2458,7 +2673,7 @@ fn fabricate_effects_module<'a>(
|
|||
|
||||
let mut var_store = VarStore::default();
|
||||
|
||||
let module_ids = { (*module_ids).lock().clone() };
|
||||
let module_ids = { (*module_ids).lock().clone() }.into_module_ids();
|
||||
|
||||
let mut scope = roc_can::scope::Scope::new(module_id, &mut var_store);
|
||||
let mut can_env = roc_can::env::Env::new(module_id, dep_idents, &module_ids, exposed_ident_ids);
|
||||
|
@ -2782,7 +2997,7 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
|
|||
Ok(Msg::Parsed(parsed))
|
||||
}
|
||||
|
||||
fn exposed_from_import(entry: &ImportsEntry<'_>) -> (ModuleName, Vec<Ident>) {
|
||||
fn exposed_from_import<'a>(entry: &ImportsEntry<'a>) -> (QualifiedModuleName<'a>, Vec<Ident>) {
|
||||
use roc_parse::header::ImportsEntry::*;
|
||||
|
||||
match entry {
|
||||
|
@ -2793,11 +3008,27 @@ fn exposed_from_import(entry: &ImportsEntry<'_>) -> (ModuleName, Vec<Ident>) {
|
|||
exposed.push(ident_from_exposed(&loc_entry.value));
|
||||
}
|
||||
|
||||
(module_name.as_str().into(), exposed)
|
||||
let qualified_module_name = QualifiedModuleName {
|
||||
opt_package: None,
|
||||
module: module_name.as_str().into(),
|
||||
};
|
||||
|
||||
(qualified_module_name, exposed)
|
||||
}
|
||||
|
||||
Package(_package_name, _module_name, _exposes) => {
|
||||
todo!("TODO support exposing package-qualified module names.");
|
||||
Package(package_name, module_name, exposes) => {
|
||||
let mut exposed = Vec::with_capacity(exposes.len());
|
||||
|
||||
for loc_entry in exposes {
|
||||
exposed.push(ident_from_exposed(&loc_entry.value));
|
||||
}
|
||||
|
||||
let qualified_module_name = QualifiedModuleName {
|
||||
opt_package: Some(package_name),
|
||||
module: module_name.as_str().into(),
|
||||
};
|
||||
|
||||
(qualified_module_name, exposed)
|
||||
}
|
||||
|
||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => {
|
||||
|
@ -3074,6 +3305,7 @@ fn run_task<'a>(
|
|||
LoadModule {
|
||||
module_name,
|
||||
module_ids,
|
||||
shorthands,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
} => load_module(
|
||||
|
@ -3081,15 +3313,24 @@ fn run_task<'a>(
|
|||
src_dir,
|
||||
module_name,
|
||||
module_ids,
|
||||
shorthands,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
)
|
||||
.map(|(_, msg)| msg),
|
||||
LoadPkgConfig {
|
||||
shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
} => load_pkg_config(arena, src_dir, module_ids, ident_ids_by_module, mode),
|
||||
} => load_pkg_config(
|
||||
arena,
|
||||
src_dir,
|
||||
shorthand,
|
||||
module_ids,
|
||||
ident_ids_by_module,
|
||||
mode,
|
||||
),
|
||||
Parse { header } => parse(arena, header),
|
||||
CanonicalizeAndConstrain {
|
||||
parsed,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
app "quicksort" provides [ swap, partition, partitionHelp, quicksort ] to "./platform"
|
||||
|
||||
quicksort : List (Num a), Int, Int -> List (Num a)
|
||||
quicksort : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksort = \list, low, high ->
|
||||
when partition low high list is
|
||||
Pair partitionIndex partitioned ->
|
||||
|
@ -9,7 +9,7 @@ quicksort = \list, low, high ->
|
|||
|> quicksort (partitionIndex + 1) high
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -21,7 +21,7 @@ swap = \i, j, list ->
|
|||
[]
|
||||
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -33,7 +33,7 @@ partition = \low, high, initialList ->
|
|||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "quicksort" provides [ quicksort ] to "./platform"
|
||||
|
||||
quicksort = \originalList ->
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
|
@ -13,7 +13,7 @@ quicksort = \originalList ->
|
|||
list
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -24,7 +24,7 @@ quicksort = \originalList ->
|
|||
_ ->
|
||||
[]
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -36,7 +36,7 @@ quicksort = \originalList ->
|
|||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
|
|
@ -2,7 +2,7 @@ interface Quicksort
|
|||
exposes [ swap, partition, quicksort ]
|
||||
imports []
|
||||
|
||||
quicksort : List (Num a), Int, Int -> List (Num a)
|
||||
quicksort : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksort = \list, low, high ->
|
||||
when partition low high list is
|
||||
Pair partitionIndex partitioned ->
|
||||
|
@ -11,7 +11,7 @@ quicksort = \list, low, high ->
|
|||
|> quicksort (partitionIndex + 1) high
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -23,7 +23,7 @@ swap = \i, j, list ->
|
|||
[]
|
||||
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -35,7 +35,7 @@ partition = \low, high, initialList ->
|
|||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
|
|
@ -265,7 +265,7 @@ mod test_load {
|
|||
imports [ RBTree ]
|
||||
provides [ main ] to blah
|
||||
|
||||
empty : RBTree.Dict Int Int
|
||||
empty : RBTree.Dict I64 I64
|
||||
empty = RBTree.empty
|
||||
|
||||
main = empty
|
||||
|
@ -360,7 +360,7 @@ mod test_load {
|
|||
"floatTest" => "F64",
|
||||
"divisionFn" => "F64, F64 -> Result F64 [ DivByZero ]*",
|
||||
"divisionTest" => "Result F64 [ DivByZero ]*",
|
||||
"intTest" => "Int",
|
||||
"intTest" => "I64",
|
||||
"x" => "F64",
|
||||
"constantNum" => "Num *",
|
||||
"divDep1ByDep2" => "Result F64 [ DivByZero ]*",
|
||||
|
@ -377,10 +377,10 @@ mod test_load {
|
|||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"swap" => "Int, Int, List a -> List a",
|
||||
"partition" => "Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]",
|
||||
"partitionHelp" => "Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ]",
|
||||
"quicksort" => "List (Num a), Int, Int -> List (Num a)",
|
||||
"swap" => "I64, I64, List a -> List a",
|
||||
"partition" => "I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]",
|
||||
"partitionHelp" => "I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]",
|
||||
"quicksort" => "List (Num a), I64, I64 -> List (Num a)",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -406,10 +406,10 @@ mod test_load {
|
|||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"swap" => "Int, Int, List a -> List a",
|
||||
"partition" => "Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]",
|
||||
"partitionHelp" => "Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ]",
|
||||
"quicksort" => "List (Num a), Int, Int -> List (Num a)",
|
||||
"swap" => "I64, I64, List a -> List a",
|
||||
"partition" => "I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]",
|
||||
"partitionHelp" => "I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]",
|
||||
"quicksort" => "List (Num a), I64, I64 -> List (Num a)",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -235,7 +235,7 @@ mod test_uniq_load {
|
|||
"floatTest" => "Attr Shared F64",
|
||||
"divisionFn" => "Attr Shared (Attr * F64, Attr * F64 -> Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*)))",
|
||||
"divisionTest" => "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))",
|
||||
"intTest" => "Attr * Int",
|
||||
"intTest" => "Attr * I64",
|
||||
"x" => "Attr * F64",
|
||||
"constantNum" => "Attr * (Num (Attr * *))",
|
||||
"divDep1ByDep2" => "Attr * (Result (Attr * F64) (Attr * [ DivByZero ]*))",
|
||||
|
@ -271,11 +271,11 @@ mod test_uniq_load {
|
|||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"swap" => "Attr * (Attr * Int, Attr * Int, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))",
|
||||
"partition" => "Attr * (Attr Shared Int, Attr Shared Int, Attr b (List (Attr Shared (Num (Attr Shared a)))) -> Attr * [ Pair (Attr * Int) (Attr b (List (Attr Shared (Num (Attr Shared a))))) ])",
|
||||
"swap" => "Attr * (Attr * I64, Attr * I64, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))",
|
||||
"partition" => "Attr * (Attr Shared I64, Attr Shared I64, Attr b (List (Attr Shared (Num (Attr Shared a)))) -> Attr * [ Pair (Attr * I64) (Attr b (List (Attr Shared (Num (Attr Shared a))))) ])",
|
||||
|
||||
"partitionHelp" => "Attr Shared (Attr b Int, Attr Shared Int, Attr c (List (Attr Shared (Num (Attr Shared a)))), Attr Shared Int, Attr Shared (Num (Attr Shared a)) -> Attr * [ Pair (Attr b Int) (Attr c (List (Attr Shared (Num (Attr Shared a))))) ])",
|
||||
"quicksort" => "Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared Int, Attr Shared Int -> Attr b (List (Attr Shared (Num (Attr Shared a)))))",
|
||||
"partitionHelp" => "Attr Shared (Attr b I64, Attr Shared I64, Attr c (List (Attr Shared (Num (Attr Shared a)))), Attr Shared I64, Attr Shared (Num (Attr Shared a)) -> Attr * [ Pair (Attr b I64) (Attr c (List (Attr Shared (Num (Attr Shared a))))) ])",
|
||||
"quicksort" => "Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared I64, Attr Shared I64 -> Attr b (List (Attr Shared (Num (Attr Shared a)))))",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,11 @@ impl Ident {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct QualifiedModuleName<'a> {
|
||||
pub opt_package: Option<&'a str>,
|
||||
pub module: ModuleName,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct ModuleName(InlinableString);
|
||||
|
||||
|
|
|
@ -310,6 +310,107 @@ impl fmt::Debug for ModuleId {
|
|||
}
|
||||
}
|
||||
|
||||
/// base.Task
|
||||
/// 1. build mapping from short name to package
|
||||
/// 2. when adding new modules from package we need to register them in some other map (this module id goes with short name) (shortname, module-name) -> moduleId
|
||||
/// 3. pass this around to other modules getting headers parsed. when parsing interfaces we need to use this map to reference shortnames
|
||||
/// 4. throw away short names. stash the module id in the can env under the resolved module name
|
||||
/// 5. test:
|
||||
|
||||
/// Package-qualified module name
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum PQModuleName<'a> {
|
||||
Unqualified(InlinableString),
|
||||
Qualified(&'a str, InlinableString),
|
||||
}
|
||||
|
||||
impl<'a> PQModuleName<'a> {
|
||||
pub fn to_module_name(&self) -> &InlinableString {
|
||||
match self {
|
||||
PQModuleName::Unqualified(name) => name,
|
||||
PQModuleName::Qualified(_, name) => name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PackageModuleIds<'a> {
|
||||
by_name: MutMap<PQModuleName<'a>, ModuleId>,
|
||||
by_id: Vec<PQModuleName<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> PackageModuleIds<'a> {
|
||||
pub fn get_or_insert(&mut self, module_name: &PQModuleName<'a>) -> ModuleId {
|
||||
match self.by_name.get(module_name) {
|
||||
Some(id) => *id,
|
||||
None => {
|
||||
let by_id = &mut self.by_id;
|
||||
let module_id = ModuleId(by_id.len() as u32);
|
||||
|
||||
by_id.push(module_name.clone());
|
||||
|
||||
self.by_name.insert(module_name.clone(), module_id);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
Self::insert_debug_name(module_id, &module_name);
|
||||
}
|
||||
|
||||
module_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_module_ids(self) -> ModuleIds {
|
||||
let by_name: MutMap<InlinableString, ModuleId> = self
|
||||
.by_name
|
||||
.into_iter()
|
||||
.map(|(pqname, module_id)| (pqname.to_module_name().clone(), module_id))
|
||||
.collect();
|
||||
|
||||
let by_id: Vec<InlinableString> = self
|
||||
.by_id
|
||||
.into_iter()
|
||||
.map(|pqname| pqname.to_module_name().clone())
|
||||
.collect();
|
||||
|
||||
ModuleIds { by_name, by_id }
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn insert_debug_name(module_id: ModuleId, module_name: &PQModuleName) {
|
||||
let mut names = DEBUG_MODULE_ID_NAMES.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked.");
|
||||
|
||||
if !names.contains_key(&module_id.0) {
|
||||
match module_name {
|
||||
PQModuleName::Unqualified(module) => {
|
||||
names.insert(module_id.0, module.to_string().into());
|
||||
}
|
||||
PQModuleName::Qualified(package, module) => {
|
||||
let name = format!("{}.{}", package, module).into();
|
||||
names.insert(module_id.0, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn insert_debug_name(_module_id: ModuleId, _module_name: &PQModuleName) {
|
||||
// By design, this is a no-op in release builds!
|
||||
}
|
||||
|
||||
pub fn get_id(&self, module_name: &PQModuleName<'a>) -> Option<&ModuleId> {
|
||||
self.by_name.get(module_name)
|
||||
}
|
||||
|
||||
pub fn get_name(&self, id: ModuleId) -> Option<&PQModuleName> {
|
||||
self.by_id.get(id.0 as usize)
|
||||
}
|
||||
|
||||
pub fn available_modules(&self) -> impl Iterator<Item = &PQModuleName> {
|
||||
self.by_id.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores a mapping between ModuleId and InlinableString.
|
||||
///
|
||||
/// Each module name is stored twice, for faster lookups.
|
||||
|
@ -346,8 +447,11 @@ impl ModuleIds {
|
|||
fn insert_debug_name(module_id: ModuleId, module_name: &InlinableString) {
|
||||
let mut names = DEBUG_MODULE_ID_NAMES.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked.");
|
||||
|
||||
// TODO make sure modules are never added more than once!
|
||||
if !names.contains_key(&module_id.0) {
|
||||
names.insert(module_id.0, module_name.to_string().into());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn insert_debug_name(_module_id: ModuleId, _module_name: &InlinableString) {
|
||||
|
@ -495,7 +599,8 @@ macro_rules! define_builtins {
|
|||
if cfg!(debug_assertions) {
|
||||
let module_id = ModuleId($module_id);
|
||||
|
||||
ModuleIds::insert_debug_name(module_id, &$module_name.into());
|
||||
let name = PQModuleName::Unqualified($module_name.into());
|
||||
PackageModuleIds::insert_debug_name(module_id, &name);
|
||||
module_id.register_debug_idents(&ident_ids);
|
||||
}
|
||||
|
||||
|
@ -551,6 +656,34 @@ macro_rules! define_builtins {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for PackageModuleIds<'a> {
|
||||
fn default() -> Self {
|
||||
// +1 because the user will be compiling at least 1 non-builtin module!
|
||||
let capacity = $total + 1;
|
||||
|
||||
let mut by_name = HashMap::with_capacity_and_hasher(capacity, default_hasher());
|
||||
let mut by_id = Vec::with_capacity(capacity);
|
||||
|
||||
let mut insert_both = |id: ModuleId, name_str: &'static str| {
|
||||
let raw_name: InlinableString = name_str.into();
|
||||
let name = PQModuleName::Unqualified(raw_name);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
Self::insert_debug_name(id, &name);
|
||||
}
|
||||
|
||||
by_name.insert(name.clone(), id);
|
||||
by_id.push(name);
|
||||
};
|
||||
|
||||
$(
|
||||
insert_both(ModuleId($module_id), $module_name);
|
||||
)+
|
||||
|
||||
PackageModuleIds { by_name, by_id }
|
||||
}
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
$(
|
||||
$(
|
||||
|
@ -609,7 +742,7 @@ define_builtins! {
|
|||
1 NUM: "Num" => {
|
||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||
1 NUM_AT_NUM: "@Num" // the Num.@Num private tag
|
||||
2 NUM_INT: "Int" imported // the Int.Int type alias
|
||||
2 NUM_I64: "I64" imported // the Num.I64 type alias
|
||||
3 NUM_INTEGER: "Integer" imported // Int : Num Integer
|
||||
4 NUM_AT_INTEGER: "@Integer" // the Int.@Integer private tag
|
||||
5 NUM_F64: "F64" imported // the Num.F64 type alias
|
||||
|
|
|
@ -323,7 +323,7 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
Structure(flat_type) => layout_from_flat_type(env, flat_type),
|
||||
|
||||
Alias(Symbol::NUM_INT, args, _) => {
|
||||
Alias(Symbol::NUM_I64, args, _) => {
|
||||
debug_assert!(args.is_empty());
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
|
@ -723,7 +723,7 @@ fn layout_from_flat_type<'a>(
|
|||
match flat_type {
|
||||
Apply(symbol, args) => {
|
||||
match symbol {
|
||||
Symbol::NUM_INT => {
|
||||
Symbol::NUM_I64 => {
|
||||
debug_assert_eq!(args.len(), 0);
|
||||
Ok(Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
|
|
|
@ -375,7 +375,7 @@ mod test_mono {
|
|||
fn ir_when_just() {
|
||||
compiles_to_ir(
|
||||
r#"
|
||||
x : [ Nothing, Just Int ]
|
||||
x : [ Nothing, Just I64 ]
|
||||
x = Just 41
|
||||
|
||||
when x is
|
||||
|
@ -412,7 +412,7 @@ mod test_mono {
|
|||
fn one_element_tag() {
|
||||
compiles_to_ir(
|
||||
r#"
|
||||
x : [ Pair Int ]
|
||||
x : [ Pair I64 ]
|
||||
x = Pair 2
|
||||
|
||||
x
|
||||
|
@ -502,7 +502,7 @@ mod test_mono {
|
|||
r#"
|
||||
Maybe a : [ Nothing, Just a ]
|
||||
|
||||
x : Maybe (Maybe Int)
|
||||
x : Maybe (Maybe I64)
|
||||
x = Just (Just 41)
|
||||
|
||||
when x is
|
||||
|
@ -795,7 +795,7 @@ mod test_mono {
|
|||
compiles_to_ir(
|
||||
r#"
|
||||
wrapper = \{} ->
|
||||
x : Result Int Int
|
||||
x : Result I64 I64
|
||||
x = Ok 2
|
||||
|
||||
y =
|
||||
|
@ -1002,10 +1002,10 @@ mod test_mono {
|
|||
compiles_to_ir(
|
||||
indoc!(
|
||||
r#"
|
||||
x : List Int
|
||||
x : List I64
|
||||
x = [1,2,3]
|
||||
|
||||
id : List Int -> List Int
|
||||
id : List I64 -> List I64
|
||||
id = \y -> List.set y 0 0
|
||||
|
||||
id x
|
||||
|
@ -1185,7 +1185,7 @@ mod test_mono {
|
|||
compiles_to_ir(
|
||||
indoc!(
|
||||
r#"
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
(Pair partitionIndex partitioned) = Pair 0 []
|
||||
|
@ -1376,7 +1376,7 @@ mod test_mono {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
@ -1412,7 +1412,7 @@ mod test_mono {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
(Pair partitionIndex partitioned) = partition low high list
|
||||
|
@ -1424,7 +1424,7 @@ mod test_mono {
|
|||
list
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -1435,7 +1435,7 @@ mod test_mono {
|
|||
_ ->
|
||||
[]
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -1447,7 +1447,7 @@ mod test_mono {
|
|||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
@ -1690,10 +1690,10 @@ mod test_mono {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
x : List Int
|
||||
x : List I64
|
||||
x = [1,2,3]
|
||||
|
||||
add : List Int -> List Int
|
||||
add : List I64 -> List I64
|
||||
add = \y -> List.set y 0 0
|
||||
|
||||
main =
|
||||
|
@ -1851,7 +1851,6 @@ mod test_mono {
|
|||
let Test.2 = S Test.9 Test.8;
|
||||
let Test.5 = 1i64;
|
||||
let Test.6 = Index 0 Test.2;
|
||||
dec Test.2;
|
||||
let Test.7 = lowlevel Eq Test.5 Test.6;
|
||||
if Test.7 then
|
||||
let Test.3 = 0i64;
|
||||
|
@ -1903,15 +1902,12 @@ mod test_mono {
|
|||
let Test.10 = lowlevel Eq Test.8 Test.9;
|
||||
if Test.10 then
|
||||
let Test.4 = Index 1 Test.2;
|
||||
dec Test.2;
|
||||
let Test.3 = 1i64;
|
||||
ret Test.3;
|
||||
else
|
||||
dec Test.2;
|
||||
let Test.5 = 0i64;
|
||||
ret Test.5;
|
||||
else
|
||||
dec Test.2;
|
||||
let Test.6 = 0i64;
|
||||
ret Test.6;
|
||||
"#
|
||||
|
@ -1996,7 +1992,7 @@ mod test_mono {
|
|||
r#"
|
||||
Maybe a : [ Nothing, Just a ]
|
||||
|
||||
x : Maybe (Maybe Int)
|
||||
x : Maybe (Maybe I64)
|
||||
x = Just (Just 41)
|
||||
|
||||
when x is
|
||||
|
@ -2051,10 +2047,10 @@ mod test_mono {
|
|||
r#"
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
nil : LinkedList Int
|
||||
nil : LinkedList I64
|
||||
nil = Nil
|
||||
|
||||
length : LinkedList a -> Int
|
||||
length : LinkedList a -> I64
|
||||
length = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
|
@ -2106,7 +2102,7 @@ mod test_mono {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
|
|
@ -1608,8 +1608,8 @@ fn to_diff<'b>(
|
|||
let right = to_doc(alloc, Parens::Unnecessary, type2);
|
||||
|
||||
let is_int = |t: &ErrorType| match t {
|
||||
ErrorType::Type(Symbol::NUM_INT, _) => true,
|
||||
ErrorType::Alias(Symbol::NUM_INT, _, _) => true,
|
||||
ErrorType::Type(Symbol::NUM_I64, _) => true,
|
||||
ErrorType::Alias(Symbol::NUM_I64, _, _) => true,
|
||||
|
||||
ErrorType::Type(Symbol::NUM_NUM, args) => match &args.get(0) {
|
||||
Some(ErrorType::Type(Symbol::NUM_INTEGER, _)) => true,
|
||||
|
|
|
@ -623,7 +623,7 @@ mod test_reporting {
|
|||
Num
|
||||
Set
|
||||
Result
|
||||
Int
|
||||
F64
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
@ -634,7 +634,7 @@ mod test_reporting {
|
|||
// report_problem_as(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// foo : Int as Int
|
||||
// foo : I64 as I64
|
||||
// foo = 42
|
||||
//
|
||||
// foo
|
||||
|
@ -657,7 +657,7 @@ mod test_reporting {
|
|||
// report_problem_as(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// foo : Int as a
|
||||
// foo : I64 as a
|
||||
// foo = 42
|
||||
//
|
||||
// foo
|
||||
|
@ -961,7 +961,7 @@ mod test_reporting {
|
|||
r#"
|
||||
bar = { bar : 0x3 }
|
||||
|
||||
f : { foo : Int } -> Bool
|
||||
f : { foo : I64 } -> Bool
|
||||
f = \_ -> True
|
||||
|
||||
f bar
|
||||
|
@ -978,11 +978,11 @@ mod test_reporting {
|
|||
|
||||
This `bar` value is a:
|
||||
|
||||
{ bar : Int }
|
||||
{ bar : I64 }
|
||||
|
||||
But `f` needs the 1st argument to be:
|
||||
|
||||
{ foo : Int }
|
||||
{ foo : I64 }
|
||||
|
||||
Tip: Seems like a record field typo. Maybe `bar` should be `foo`?
|
||||
|
||||
|
@ -1037,7 +1037,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : [ Red Int, Green Bool ] -> Bool
|
||||
f : [ Red I64, Green Bool ] -> Bool
|
||||
f = \_ -> True
|
||||
|
||||
f (Blue 3.14)
|
||||
|
@ -1058,7 +1058,7 @@ mod test_reporting {
|
|||
|
||||
But `f` needs the 1st argument to be:
|
||||
|
||||
[ Green Bool, Red Int ]
|
||||
[ Green Bool, Red I64 ]
|
||||
|
||||
Tip: Seems like a tag typo. Maybe `Blue` should be `Red`?
|
||||
|
||||
|
@ -1075,7 +1075,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Int
|
||||
x : I64
|
||||
x = if True then 3.14 else 4
|
||||
|
||||
x
|
||||
|
@ -1096,7 +1096,7 @@ mod test_reporting {
|
|||
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
Int
|
||||
I64
|
||||
|
||||
Tip: You can convert between Int and Float using functions like
|
||||
`Num.toFloat` and `Num.round`.
|
||||
|
@ -1110,7 +1110,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Int
|
||||
x : I64
|
||||
x =
|
||||
when True is
|
||||
_ -> 3.14
|
||||
|
@ -1124,7 +1124,7 @@ mod test_reporting {
|
|||
|
||||
Something is off with the body of the `x` definition:
|
||||
|
||||
1│ x : Int
|
||||
1│ x : I64
|
||||
2│ x =
|
||||
3│> when True is
|
||||
4│> _ -> 3.14
|
||||
|
@ -1135,7 +1135,7 @@ mod test_reporting {
|
|||
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
Int
|
||||
I64
|
||||
|
||||
Tip: You can convert between Int and Float using functions like
|
||||
`Num.toFloat` and `Num.round`.
|
||||
|
@ -1149,7 +1149,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Int -> Int
|
||||
x : I64 -> I64
|
||||
x = \_ -> 3.14
|
||||
|
||||
x
|
||||
|
@ -1161,7 +1161,7 @@ mod test_reporting {
|
|||
|
||||
Something is off with the body of the `x` definition:
|
||||
|
||||
1│ x : Int -> Int
|
||||
1│ x : I64 -> I64
|
||||
2│ x = \_ -> 3.14
|
||||
^^^^
|
||||
|
||||
|
@ -1171,7 +1171,7 @@ mod test_reporting {
|
|||
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
Int
|
||||
I64
|
||||
|
||||
Tip: You can convert between Int and Float using functions like
|
||||
`Num.toFloat` and `Num.round`.
|
||||
|
@ -1185,7 +1185,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Int
|
||||
x : I64
|
||||
x = 42
|
||||
|
||||
x 3
|
||||
|
@ -1211,7 +1211,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : Int -> Int
|
||||
f : I64 -> I64
|
||||
f = \_ -> 42
|
||||
|
||||
f 1 2
|
||||
|
@ -1237,7 +1237,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : Int, Int -> Int
|
||||
f : I64, I64 -> I64
|
||||
f = \_, _ -> 42
|
||||
|
||||
f 1
|
||||
|
@ -1372,9 +1372,9 @@ mod test_reporting {
|
|||
these names seem close though:
|
||||
|
||||
Bool
|
||||
Int
|
||||
F64
|
||||
Num
|
||||
Map
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -1483,7 +1483,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x } : { x : Int }
|
||||
{ x } : { x : I64 }
|
||||
{ x } = { x: 4.0 }
|
||||
|
||||
x
|
||||
|
@ -1495,7 +1495,7 @@ mod test_reporting {
|
|||
|
||||
Something is off with the body of this definition:
|
||||
|
||||
1│ { x } : { x : Int }
|
||||
1│ { x } : { x : I64 }
|
||||
2│ { x } = { x: 4.0 }
|
||||
^^^^^^^^^^
|
||||
|
||||
|
@ -1505,7 +1505,7 @@ mod test_reporting {
|
|||
|
||||
But the type annotation says it should be:
|
||||
|
||||
{ x : Int }
|
||||
{ x : I64 }
|
||||
|
||||
Tip: You can convert between Int and Float using functions like
|
||||
`Num.toFloat` and `Num.round`.
|
||||
|
@ -1644,7 +1644,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x : { a : Int, b : F64, c : Bool }
|
||||
x : { a : I64, b : F64, c : Bool }
|
||||
x = { b: 4.0 }
|
||||
|
||||
x
|
||||
|
@ -1656,7 +1656,7 @@ mod test_reporting {
|
|||
|
||||
Something is off with the body of the `x` definition:
|
||||
|
||||
1│ x : { a : Int, b : F64, c : Bool }
|
||||
1│ x : { a : I64, b : F64, c : Bool }
|
||||
2│ x = { b: 4.0 }
|
||||
^^^^^^^^^^
|
||||
|
||||
|
@ -1666,7 +1666,7 @@ mod test_reporting {
|
|||
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
{ a : Int, b : F64, c : Bool }
|
||||
{ a : I64, b : F64, c : Bool }
|
||||
|
||||
Tip: Looks like the c and a fields are missing.
|
||||
"#
|
||||
|
@ -1788,7 +1788,7 @@ mod test_reporting {
|
|||
|
||||
The body is an integer of type:
|
||||
|
||||
Int
|
||||
I64
|
||||
|
||||
But the type annotation on `f` says it should be:
|
||||
|
||||
|
@ -1796,7 +1796,7 @@ mod test_reporting {
|
|||
|
||||
Tip: The type annotation uses the type variable `msg` to say that this
|
||||
definition can produce any type of value. But in the body I see that
|
||||
it will only produce a `Int` value of a single specific type. Maybe
|
||||
it will only produce a `I64` value of a single specific type. Maybe
|
||||
change the type annotation to be more specific? Maybe change the code
|
||||
to be more general?
|
||||
"#
|
||||
|
@ -1810,7 +1810,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : Bool -> [ Ok Int, InvalidFoo ]
|
||||
f : Bool -> [ Ok I64, InvalidFoo ]
|
||||
f = \_ -> ok 4
|
||||
|
||||
f
|
||||
|
@ -1828,9 +1828,9 @@ mod test_reporting {
|
|||
these names seem close though:
|
||||
|
||||
f
|
||||
Int
|
||||
F64
|
||||
Num
|
||||
Map
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -1842,7 +1842,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : Bool -> Int
|
||||
f : Bool -> I64
|
||||
f = \_ ->
|
||||
ok = 3
|
||||
|
||||
|
@ -1867,7 +1867,7 @@ mod test_reporting {
|
|||
|
||||
Something is off with the body of the `f` definition:
|
||||
|
||||
1│ f : Bool -> Int
|
||||
1│ f : Bool -> I64
|
||||
2│ f = \_ ->
|
||||
3│ ok = 3
|
||||
4│
|
||||
|
@ -1880,7 +1880,7 @@ mod test_reporting {
|
|||
|
||||
But the type annotation on `f` says it should be:
|
||||
|
||||
Int
|
||||
I64
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -2005,7 +2005,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : { fo: Int }ext -> Int
|
||||
f : { fo: I64 }ext -> I64
|
||||
f = \r ->
|
||||
r2 = { r & foo: r.fo }
|
||||
|
||||
|
@ -2026,7 +2026,7 @@ mod test_reporting {
|
|||
|
||||
This is usually a typo. Here are the `r` fields that are most similar:
|
||||
|
||||
{ fo : Int
|
||||
{ fo : I64
|
||||
}ext
|
||||
|
||||
So maybe `.foo` should be `.fo`?
|
||||
|
@ -2259,12 +2259,12 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
Either : [ Left Int, Right Bool ]
|
||||
Either : [ Left I64, Right Bool ]
|
||||
|
||||
x : Either
|
||||
x = Left 42
|
||||
|
||||
f : Either -> Int
|
||||
f : Either -> I64
|
||||
f = \Left v -> v
|
||||
|
||||
f x
|
||||
|
@ -2296,7 +2296,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
x : [ Left Int, Right Bool ]
|
||||
x : [ Left I64, Right Bool ]
|
||||
x = Left 42
|
||||
|
||||
|
||||
|
@ -2425,7 +2425,7 @@ mod test_reporting {
|
|||
r#"
|
||||
RemoteData e a : [ NotAsked, Loading, Failure e, Success a ]
|
||||
|
||||
x : RemoteData Int Str
|
||||
x : RemoteData I64 Str
|
||||
|
||||
when x is
|
||||
NotAsked -> 3
|
||||
|
@ -2488,7 +2488,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
y : [ Nothing, Just Int ]
|
||||
y : [ Nothing, Just I64 ]
|
||||
y = Just 4
|
||||
x = { a: y, b: 42}
|
||||
|
||||
|
@ -2581,9 +2581,9 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
Foo : { x : Int }
|
||||
Foo : { x : I64 }
|
||||
|
||||
f : Foo -> Int
|
||||
f : Foo -> I64
|
||||
f = \r -> r.x
|
||||
|
||||
f { y: 3.14 }
|
||||
|
@ -2605,7 +2605,7 @@ mod test_reporting {
|
|||
|
||||
But `f` needs the 1st argument to be:
|
||||
|
||||
{ x : Int }
|
||||
{ x : I64 }
|
||||
|
||||
Tip: Seems like a record field typo. Maybe `y` should be `x`?
|
||||
|
||||
|
@ -2835,7 +2835,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
a : { foo : Int, bar : F64, foo : Str }
|
||||
a : { foo : I64, bar : F64, foo : Str }
|
||||
a = { bar: 3.0, foo: "foo" }
|
||||
|
||||
a
|
||||
|
@ -2847,12 +2847,12 @@ mod test_reporting {
|
|||
|
||||
This record type defines the `.foo` field twice!
|
||||
|
||||
1│ a : { foo : Int, bar : F64, foo : Str }
|
||||
1│ a : { foo : I64, bar : F64, foo : Str }
|
||||
^^^^^^^^^ ^^^^^^^^^
|
||||
|
||||
In the rest of the program, I will only use the latter definition:
|
||||
|
||||
1│ a : { foo : Int, bar : F64, foo : Str }
|
||||
1│ a : { foo : I64, bar : F64, foo : Str }
|
||||
^^^^^^^^^
|
||||
|
||||
For clarity, remove the previous `.foo` definitions from this record
|
||||
|
@ -2867,7 +2867,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
a : [ Foo Int, Bar F64, Foo Str ]
|
||||
a : [ Foo I64, Bar F64, Foo Str ]
|
||||
a = Foo "foo"
|
||||
|
||||
a
|
||||
|
@ -2879,12 +2879,12 @@ mod test_reporting {
|
|||
|
||||
This tag union type defines the `Foo` tag twice!
|
||||
|
||||
1│ a : [ Foo Int, Bar F64, Foo Str ]
|
||||
1│ a : [ Foo I64, Bar F64, Foo Str ]
|
||||
^^^^^^^ ^^^^^^^
|
||||
|
||||
In the rest of the program, I will only use the latter definition:
|
||||
|
||||
1│ a : [ Foo Int, Bar F64, Foo Str ]
|
||||
1│ a : [ Foo I64, Bar F64, Foo Str ]
|
||||
^^^^^^^
|
||||
|
||||
For clarity, remove the previous `Foo` definitions from this tag union
|
||||
|
@ -2899,7 +2899,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
bar : Int
|
||||
bar : I64
|
||||
foo = \x -> x
|
||||
|
||||
# NOTE: neither bar or foo are defined at this point
|
||||
|
@ -2913,7 +2913,7 @@ mod test_reporting {
|
|||
This annotation does not match the definition immediately following
|
||||
it:
|
||||
|
||||
1│> bar : Int
|
||||
1│> bar : I64
|
||||
2│> foo = \x -> x
|
||||
|
||||
Is it a typo? If not, put either a newline or comment between them.
|
||||
|
@ -2927,7 +2927,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
bar : Int
|
||||
bar : I64
|
||||
|
||||
foo = \x -> x
|
||||
|
||||
|
@ -2943,7 +2943,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
MyAlias 1 : Int
|
||||
MyAlias 1 : I64
|
||||
|
||||
4
|
||||
"#
|
||||
|
@ -2954,7 +2954,7 @@ mod test_reporting {
|
|||
|
||||
This pattern in the definition of `MyAlias` is not what I expect:
|
||||
|
||||
1│ MyAlias 1 : Int
|
||||
1│ MyAlias 1 : I64
|
||||
^
|
||||
|
||||
Only type variables like `a` or `value` can occur in this position.
|
||||
|
@ -2963,7 +2963,7 @@ mod test_reporting {
|
|||
|
||||
`MyAlias` is not used anywhere in your code.
|
||||
|
||||
1│ MyAlias 1 : Int
|
||||
1│ MyAlias 1 : I64
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
If you didn't intend on using `MyAlias` then remove it so future readers
|
||||
|
@ -2978,7 +2978,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
a : Num Int F64
|
||||
a : Num I64 F64
|
||||
a = 3
|
||||
|
||||
a
|
||||
|
@ -2990,7 +2990,7 @@ mod test_reporting {
|
|||
|
||||
The `Num` alias expects 1 type argument, but it got 2 instead:
|
||||
|
||||
1│ a : Num Int F64
|
||||
1│ a : Num I64 F64
|
||||
^^^^^^^^^^^
|
||||
|
||||
Are there missing parentheses?
|
||||
|
@ -3004,7 +3004,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : Bool -> Num Int F64
|
||||
f : Bool -> Num I64 F64
|
||||
f = \_ -> 3
|
||||
|
||||
f
|
||||
|
@ -3016,7 +3016,7 @@ mod test_reporting {
|
|||
|
||||
The `Num` alias expects 1 type argument, but it got 2 instead:
|
||||
|
||||
1│ f : Bool -> Num Int F64
|
||||
1│ f : Bool -> Num I64 F64
|
||||
^^^^^^^^^^^
|
||||
|
||||
Are there missing parentheses?
|
||||
|
@ -3032,7 +3032,7 @@ mod test_reporting {
|
|||
r#"
|
||||
Pair a b : [ Pair a b ]
|
||||
|
||||
x : Pair Int
|
||||
x : Pair I64
|
||||
x = Pair 2 3
|
||||
|
||||
x
|
||||
|
@ -3044,7 +3044,7 @@ mod test_reporting {
|
|||
|
||||
The `Pair` alias expects 2 type arguments, but it got 1 instead:
|
||||
|
||||
3│ x : Pair Int
|
||||
3│ x : Pair I64
|
||||
^^^^^^^^
|
||||
|
||||
Are there missing parentheses?
|
||||
|
@ -3060,7 +3060,7 @@ mod test_reporting {
|
|||
r#"
|
||||
Pair a b : [ Pair a b ]
|
||||
|
||||
x : Pair Int Int Int
|
||||
x : Pair I64 I64 I64
|
||||
x = 3
|
||||
|
||||
x
|
||||
|
@ -3072,7 +3072,7 @@ mod test_reporting {
|
|||
|
||||
The `Pair` alias expects 2 type arguments, but it got 3 instead:
|
||||
|
||||
3│ x : Pair Int Int Int
|
||||
3│ x : Pair I64 I64 I64
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Are there missing parentheses?
|
||||
|
@ -3088,7 +3088,7 @@ mod test_reporting {
|
|||
r#"
|
||||
Foo a : [ Foo ]
|
||||
|
||||
f : Foo Int
|
||||
f : Foo I64
|
||||
|
||||
f
|
||||
"#
|
||||
|
@ -3176,7 +3176,7 @@ mod test_reporting {
|
|||
AList a b : [ ACons a (BList a b), ANil ]
|
||||
BList a b : [ BCons a (AList a b), BNil ]
|
||||
|
||||
x : AList Int Int
|
||||
x : AList I64 I64
|
||||
x = ACons 0 (BCons 1 (ACons "foo" BNil ))
|
||||
|
||||
y : BList a a
|
||||
|
@ -3191,19 +3191,19 @@ mod test_reporting {
|
|||
|
||||
Something is off with the body of the `x` definition:
|
||||
|
||||
4│ x : AList Int Int
|
||||
4│ x : AList I64 I64
|
||||
5│ x = ACons 0 (BCons 1 (ACons "foo" BNil ))
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This `ACons` global tag application has the type:
|
||||
|
||||
[ ACons (Num Integer) [ BCons (Num Integer) [ ACons Str [
|
||||
BCons Int [ ACons Int (BList Int Int), ANil ] as a, BNil ], ANil
|
||||
BCons I64 [ ACons I64 (BList I64 I64), ANil ] as a, BNil ], ANil
|
||||
], BNil ], ANil ]
|
||||
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
[ ACons Int (BList Int Int), ANil ] as a
|
||||
[ ACons I64 (BList I64 I64), ANil ] as a
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -3575,7 +3575,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : { x : Int, y ? Int } -> Int
|
||||
f : { x : I64, y ? I64 } -> I64
|
||||
f = \{ x, y ? "foo" } -> (\g, _ -> g) x y
|
||||
|
||||
f
|
||||
|
@ -3592,11 +3592,11 @@ mod test_reporting {
|
|||
|
||||
The argument is a pattern that matches record values of type:
|
||||
|
||||
{ x : Int, y ? Str }
|
||||
{ x : I64, y ? Str }
|
||||
|
||||
But the annotation on `f` says the 1st argument should be:
|
||||
|
||||
{ x : Int, y ? Int }
|
||||
{ x : I64, y ? I64 }
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -3608,7 +3608,7 @@ mod test_reporting {
|
|||
indoc!(
|
||||
r#"
|
||||
\rec ->
|
||||
{ x, y } : { x : Int, y ? Bool }
|
||||
{ x, y } : { x : I64, y ? Bool }
|
||||
{ x, y } = rec
|
||||
|
||||
{ x, y }
|
||||
|
@ -3620,16 +3620,16 @@ mod test_reporting {
|
|||
|
||||
Something is off with the body of this definition:
|
||||
|
||||
2│> { x, y } : { x : Int, y ? Bool }
|
||||
2│> { x, y } : { x : I64, y ? Bool }
|
||||
3│> { x, y } = rec
|
||||
|
||||
The body is a value of type:
|
||||
|
||||
{ x : Int, y : Bool }
|
||||
{ x : I64, y : Bool }
|
||||
|
||||
But the type annotation says it should be:
|
||||
|
||||
{ x : Int, y ? Bool }
|
||||
{ x : I64, y ? Bool }
|
||||
|
||||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||||
says this field is optional. Learn more about optional fields at TODO.
|
||||
|
@ -3643,7 +3643,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : { x : Int, y ? Int } -> Int
|
||||
f : { x : I64, y ? I64 } -> I64
|
||||
f = \{ x, y } -> x + y
|
||||
|
||||
f
|
||||
|
@ -3660,11 +3660,11 @@ mod test_reporting {
|
|||
|
||||
The argument is a pattern that matches record values of type:
|
||||
|
||||
{ x : Int, y : Int }
|
||||
{ x : I64, y : I64 }
|
||||
|
||||
But the annotation on `f` says the 1st argument should be:
|
||||
|
||||
{ x : Int, y ? Int }
|
||||
{ x : I64, y ? I64 }
|
||||
|
||||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||||
says this field is optional. Learn more about optional fields at TODO.
|
||||
|
@ -3678,7 +3678,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : { x : Int, y ? Int } -> Int
|
||||
f : { x : I64, y ? I64 } -> I64
|
||||
f = \r ->
|
||||
when r is
|
||||
{ x, y } -> x + y
|
||||
|
@ -3697,11 +3697,11 @@ mod test_reporting {
|
|||
|
||||
The first pattern is trying to match record values of type:
|
||||
|
||||
{ x : Int, y : Int }
|
||||
{ x : I64, y : I64 }
|
||||
|
||||
But the expression between `when` and `is` has the type:
|
||||
|
||||
{ x : Int, y ? Int }
|
||||
{ x : I64, y ? I64 }
|
||||
|
||||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||||
says this field is optional. Learn more about optional fields at TODO.
|
||||
|
@ -3715,7 +3715,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : { x : Int, y ? Int } -> Int
|
||||
f : { x : I64, y ? I64 } -> I64
|
||||
f = \r -> r.y
|
||||
|
||||
f
|
||||
|
@ -3732,11 +3732,11 @@ mod test_reporting {
|
|||
|
||||
This `r` value is a:
|
||||
|
||||
{ x : Int, y ? Int }
|
||||
{ x : I64, y ? I64 }
|
||||
|
||||
But you are trying to use it as:
|
||||
|
||||
{ x : Int, y : Int }
|
||||
{ x : I64, y : I64 }
|
||||
|
||||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||||
says this field is optional. Learn more about optional fields at TODO.
|
||||
|
@ -3750,7 +3750,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : { x : Int, y ? Int } -> Int
|
||||
f : { x : I64, y ? I64 } -> I64
|
||||
f = \r -> .y r
|
||||
|
||||
f
|
||||
|
@ -3767,11 +3767,11 @@ mod test_reporting {
|
|||
|
||||
This `r` value is a:
|
||||
|
||||
{ x : Int, y ? Int }
|
||||
{ x : I64, y ? I64 }
|
||||
|
||||
But this function needs the 1st argument to be:
|
||||
|
||||
{ x : Int, y : Int }
|
||||
{ x : I64, y : I64 }
|
||||
|
||||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||||
says this field is optional. Learn more about optional fields at TODO.
|
||||
|
@ -3785,7 +3785,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : { x : Int, y : Int } -> Int
|
||||
f : { x : I64, y : I64 } -> I64
|
||||
f = \r ->
|
||||
when r is
|
||||
{ x, y : "foo" } -> x + 0
|
||||
|
@ -3805,11 +3805,11 @@ mod test_reporting {
|
|||
|
||||
The first pattern is trying to match record values of type:
|
||||
|
||||
{ x : Int, y : Str }
|
||||
{ x : I64, y : Str }
|
||||
|
||||
But the expression between `when` and `is` has the type:
|
||||
|
||||
{ x : Int, y : Int }
|
||||
{ x : I64, y : I64 }
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -3820,7 +3820,7 @@ mod test_reporting {
|
|||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : { x : Int, y ? Int } -> Int
|
||||
f : { x : I64, y ? I64 } -> I64
|
||||
f = \r ->
|
||||
when r is
|
||||
{ x, y ? "foo" } -> (\g, _ -> g) x y
|
||||
|
@ -3840,11 +3840,11 @@ mod test_reporting {
|
|||
|
||||
The first pattern is trying to match record values of type:
|
||||
|
||||
{ x : Int, y ? Str }
|
||||
{ x : I64, y ? Str }
|
||||
|
||||
But the expression between `when` and `is` has the type:
|
||||
|
||||
{ x : Int, y ? Int }
|
||||
{ x : I64, y ? I64 }
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
|
|
@ -1326,12 +1326,12 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
int : Int
|
||||
int : I64
|
||||
|
||||
int
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
"I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1345,7 +1345,7 @@ mod solve_expr {
|
|||
int
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
"I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1354,13 +1354,13 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
int : Int
|
||||
int : I64
|
||||
int = 5
|
||||
|
||||
int
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
"I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1369,13 +1369,13 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
int : Num.Int
|
||||
int : Num.I64
|
||||
int = 5
|
||||
|
||||
int
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
"I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1390,7 +1390,7 @@ mod solve_expr {
|
|||
int
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
"I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1405,7 +1405,7 @@ mod solve_expr {
|
|||
int
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
"I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1489,13 +1489,13 @@ mod solve_expr {
|
|||
r#"
|
||||
Res a e : [ Okay a, Error e ]
|
||||
|
||||
ok : Res Int *
|
||||
ok : Res I64 *
|
||||
ok = Okay 5
|
||||
|
||||
ok
|
||||
"#
|
||||
),
|
||||
"Res Int *",
|
||||
"Res I64 *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1521,13 +1521,13 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
ok : Result Int *
|
||||
ok : Result I64 *
|
||||
ok = Ok 5
|
||||
|
||||
ok
|
||||
"#
|
||||
),
|
||||
"Result Int *",
|
||||
"Result I64 *",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1551,7 +1551,7 @@ mod solve_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
ok : Result Int *
|
||||
ok : Result I64 *
|
||||
ok = Ok 5
|
||||
|
||||
err : Result * Str
|
||||
|
@ -1563,7 +1563,7 @@ mod solve_expr {
|
|||
err
|
||||
"#
|
||||
),
|
||||
"Result Int Str",
|
||||
"Result I64 Str",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1584,19 +1584,19 @@ mod solve_expr {
|
|||
|
||||
// #[test]
|
||||
// fn annotation_using_num_used() {
|
||||
// // There was a problem where `Int`, because it is only an annotation
|
||||
// // There was a problem where `I64`, because it is only an annotation
|
||||
// // wasn't added to the vars_by_symbol.
|
||||
// infer_eq_without_problem(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// int : Int
|
||||
// int : I64
|
||||
|
||||
// p = (\x -> x) int
|
||||
|
||||
// p
|
||||
// "#
|
||||
// ),
|
||||
// "Int",
|
||||
// "I64",
|
||||
// );
|
||||
// }
|
||||
|
||||
|
@ -1631,7 +1631,7 @@ mod solve_expr {
|
|||
x
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
"I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1840,7 +1840,7 @@ mod solve_expr {
|
|||
v
|
||||
"#
|
||||
),
|
||||
"Foo Int",
|
||||
"Foo I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1907,7 +1907,7 @@ mod solve_expr {
|
|||
length
|
||||
"#
|
||||
),
|
||||
"Peano -> Int",
|
||||
"Peano -> I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2253,13 +2253,13 @@ mod solve_expr {
|
|||
// infer_eq_without_problem(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// UserId x : [ UserId Int ]
|
||||
// UserId x : [ UserId I64 ]
|
||||
// UserId x = UserId 42
|
||||
|
||||
// x
|
||||
// "#
|
||||
// ),
|
||||
// "Int",
|
||||
// "I64",
|
||||
// );
|
||||
// }
|
||||
|
||||
|
@ -2340,13 +2340,13 @@ mod solve_expr {
|
|||
ListB a : [ Cons a (ListC a) ]
|
||||
ListC a : [ Cons a (ListA a), Nil ]
|
||||
|
||||
val : ListC Num.Int
|
||||
val : ListC Num.I64
|
||||
val = Cons 1 (Cons 2 (Cons 3 Nil))
|
||||
|
||||
val
|
||||
"#
|
||||
),
|
||||
"ListC Int",
|
||||
"ListC I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2388,7 +2388,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
partition : Int, Int, List Int -> [ Pair Int (List Int) ]
|
||||
partition : I64, I64, List I64 -> [ Pair I64 (List I64) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok _ ->
|
||||
|
@ -2400,7 +2400,7 @@ mod solve_expr {
|
|||
partition
|
||||
"#
|
||||
),
|
||||
"Int, Int, List Int -> [ Pair Int (List Int) ]",
|
||||
"I64, I64, List I64 -> [ Pair I64 (List I64) ]",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2410,7 +2410,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -2421,7 +2421,7 @@ mod solve_expr {
|
|||
_ ->
|
||||
list
|
||||
|
||||
partition : Int, Int, List Int -> [ Pair Int (List Int) ]
|
||||
partition : I64, I64, List I64 -> [ Pair I64 (List I64) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -2449,7 +2449,7 @@ mod solve_expr {
|
|||
partition
|
||||
"#
|
||||
),
|
||||
"Int, Int, List Int -> [ Pair Int (List Int) ]",
|
||||
"I64, I64, List I64 -> [ Pair I64 (List I64) ]",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -2462,14 +2462,14 @@ mod solve_expr {
|
|||
idList : List a -> List a
|
||||
idList = \list -> list
|
||||
|
||||
foo : List Int -> List Int
|
||||
foo : List I64 -> List I64
|
||||
foo = \initialList -> idList initialList
|
||||
|
||||
|
||||
foo
|
||||
"#
|
||||
),
|
||||
"List Int -> List Int",
|
||||
"List I64 -> List I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2547,7 +2547,7 @@ mod solve_expr {
|
|||
Num.ceiling
|
||||
"#
|
||||
),
|
||||
"F64 -> Int",
|
||||
"F64 -> I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2559,7 +2559,7 @@ mod solve_expr {
|
|||
Num.floor
|
||||
"#
|
||||
),
|
||||
"F64 -> Int",
|
||||
"F64 -> I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2571,7 +2571,7 @@ mod solve_expr {
|
|||
Num.powInt
|
||||
"#
|
||||
),
|
||||
"Int, Int -> Int",
|
||||
"I64, I64 -> I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2789,15 +2789,15 @@ mod solve_expr {
|
|||
#[should_panic]
|
||||
fn rigid_record_quantification() {
|
||||
// the ext here is qualified on the outside (because we have rank 1 types, not rank 2).
|
||||
// That means e.g. `f : { bar : String, foo : Int } -> Bool }` is a valid argument, but
|
||||
// that function could not be applied to the `{ foo : Int }` list. Therefore, this function
|
||||
// That means e.g. `f : { bar : String, foo : I64 } -> Bool }` is a valid argument, but
|
||||
// that function could not be applied to the `{ foo : I64 }` list. Therefore, this function
|
||||
// is not allowed.
|
||||
//
|
||||
// should hit a debug_assert! in debug mode, and produce a type error in release mode
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
test : ({ foo : Int }ext -> Bool), { foo : Int } -> Bool
|
||||
test : ({ foo : I64 }ext -> Bool), { foo : I64 } -> Bool
|
||||
test = \fn, a -> fn a
|
||||
|
||||
test
|
||||
|
@ -2814,12 +2814,12 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
negatePoint : { x : Int, y : Int, z ? Num c } -> { x : Int, y : Int, z : Num c }
|
||||
negatePoint : { x : I64, y : I64, z ? Num c } -> { x : I64, y : I64, z : Num c }
|
||||
|
||||
negatePoint { x: 1, y: 2 }
|
||||
"#
|
||||
),
|
||||
"{ x : Int, y : Int, z : Num c }",
|
||||
"{ x : I64, y : I64, z : Num c }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2828,7 +2828,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
negatePoint : { x : Int, y : Int, z ? Num c }r -> { x : Int, y : Int, z : Num c }r
|
||||
negatePoint : { x : I64, y : I64, z ? Num c }r -> { x : I64, y : I64, z : Num c }r
|
||||
|
||||
a = negatePoint { x: 1, y: 2 }
|
||||
b = negatePoint { x: 1, y: 2, blah : "hi" }
|
||||
|
@ -2836,7 +2836,7 @@ mod solve_expr {
|
|||
{ a, b }
|
||||
"#
|
||||
),
|
||||
"{ a : { x : Int, y : Int, z : Num c }, b : { blah : Str, x : Int, y : Int, z : Num c } }",
|
||||
"{ a : { x : I64, y : I64, z : Num c }, b : { blah : Str, x : I64, y : I64, z : Num c } }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2850,7 +2850,7 @@ mod solve_expr {
|
|||
negatePoint { x: 1, y: 2.1, z: 0x3 }
|
||||
"#
|
||||
),
|
||||
"{ x : Num a, y : F64, z : Int }",
|
||||
"{ x : Num a, y : F64, z : I64 }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2917,13 +2917,13 @@ mod solve_expr {
|
|||
indoc!(
|
||||
r#"
|
||||
\rec ->
|
||||
{ x, y } : { x : Int, y ? Bool }*
|
||||
{ x, y } : { x : I64, y ? Bool }*
|
||||
{ x, y ? False } = rec
|
||||
|
||||
{ x, y }
|
||||
"#
|
||||
),
|
||||
"{ x : Int, y ? Bool }* -> { x : Int, y : Bool }",
|
||||
"{ x : I64, y ? Bool }* -> { x : I64, y : Bool }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2944,14 +2944,14 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
empty : List Int
|
||||
empty : List I64
|
||||
empty =
|
||||
[]
|
||||
|
||||
List.walkBackwards empty (\a, b -> a + b) 0
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
"I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2970,7 +2970,7 @@ mod solve_expr {
|
|||
g
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
"I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3023,7 +3023,7 @@ mod solve_expr {
|
|||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Bar : [ Bar ]
|
||||
Foo : [ Foo Bar Int, Empty ]
|
||||
Foo : [ Foo Bar I64, Empty ]
|
||||
|
||||
foo : Foo
|
||||
foo = Foo Bar 1
|
||||
|
@ -3037,7 +3037,7 @@ mod solve_expr {
|
|||
x
|
||||
"#
|
||||
),
|
||||
"[ Empty, Foo [ Bar ] Int ]",
|
||||
"[ Empty, Foo [ Bar ] I64 ]",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3048,7 +3048,7 @@ mod solve_expr {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
Foo : [ @Foo [ @Bar ] Int, @Empty ]
|
||||
Foo : [ @Foo [ @Bar ] I64, @Empty ]
|
||||
|
||||
foo : Foo
|
||||
foo = @Foo @Bar 1
|
||||
|
@ -3062,7 +3062,7 @@ mod solve_expr {
|
|||
x
|
||||
"#
|
||||
),
|
||||
"[ @Empty, @Foo [ @Bar ] Int ]",
|
||||
"[ @Empty, @Foo [ @Bar ] I64 ]",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3073,21 +3073,21 @@ mod solve_expr {
|
|||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
State a : { count : Int, x : a }
|
||||
State a : { count : I64, x : a }
|
||||
|
||||
foo : State a -> Int
|
||||
foo : State a -> I64
|
||||
foo = \state ->
|
||||
if state.count == 0 then
|
||||
0
|
||||
else
|
||||
1 + foo { count: state.count - 1, x: state.x }
|
||||
|
||||
main : Int
|
||||
main : I64
|
||||
main =
|
||||
foo { count: 3, x: {} }
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
"I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3108,15 +3108,15 @@ mod solve_expr {
|
|||
empty =
|
||||
Empty
|
||||
|
||||
foo : Dict Int Int
|
||||
foo : Dict I64 I64
|
||||
foo = empty
|
||||
|
||||
main : Dict Int Int
|
||||
main : Dict I64 I64
|
||||
main =
|
||||
foo
|
||||
"#
|
||||
),
|
||||
"Dict Int Int",
|
||||
"Dict I64 I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3370,12 +3370,12 @@ mod solve_expr {
|
|||
removeHelpEQGT targetKey (removeHelpPrepEQGT targetKey dict color key value left right)
|
||||
|
||||
|
||||
main : Dict Int Int
|
||||
main : Dict I64 I64
|
||||
main =
|
||||
removeHelp 1 Empty
|
||||
"#
|
||||
),
|
||||
"Dict Int Int",
|
||||
"Dict I64 I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3410,12 +3410,12 @@ mod solve_expr {
|
|||
Empty
|
||||
|
||||
|
||||
main : Dict Int
|
||||
main : Dict I64
|
||||
main =
|
||||
removeHelp 1 Empty
|
||||
"#
|
||||
),
|
||||
"Dict Int",
|
||||
"Dict I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3486,12 +3486,12 @@ mod solve_expr {
|
|||
|
||||
removeMin : Dict k v -> Dict k v
|
||||
|
||||
main : Dict Int Int
|
||||
main : Dict I64 I64
|
||||
main =
|
||||
removeHelp 1 Empty
|
||||
"#
|
||||
),
|
||||
"Dict Int Int",
|
||||
"Dict I64 I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3502,7 +3502,7 @@ mod solve_expr {
|
|||
r#"
|
||||
app "test" provides [ partitionHelp ] to "./platform"
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -3513,7 +3513,7 @@ mod solve_expr {
|
|||
_ ->
|
||||
[]
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
@ -3529,7 +3529,7 @@ mod solve_expr {
|
|||
Pair i list
|
||||
"#
|
||||
),
|
||||
"Int, Int, List (Num a), Int, Num a -> [ Pair Int (List (Num a)) ]",
|
||||
"I64, I64, List (Num a), I64, Num a -> [ Pair I64 (List (Num a)) ]",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3546,12 +3546,12 @@ mod solve_expr {
|
|||
balance = \key, left ->
|
||||
Node key left Empty
|
||||
|
||||
main : Dict Int
|
||||
main : Dict I64
|
||||
main =
|
||||
balance 0 Empty
|
||||
"#
|
||||
),
|
||||
"Dict Int",
|
||||
"Dict I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3570,12 +3570,12 @@ mod solve_expr {
|
|||
balance = \key, left ->
|
||||
node key left Empty
|
||||
|
||||
main : Dict Int
|
||||
main : Dict I64
|
||||
main =
|
||||
balance 0 Empty
|
||||
"#
|
||||
),
|
||||
"Dict Int",
|
||||
"Dict I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3619,12 +3619,12 @@ mod solve_expr {
|
|||
_ ->
|
||||
Node color key value left right
|
||||
|
||||
main : Dict Int Int
|
||||
main : Dict I64 I64
|
||||
main =
|
||||
balance Red 0 0 Empty Empty
|
||||
"#
|
||||
),
|
||||
"Dict Int Int",
|
||||
"Dict I64 I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3648,12 +3648,12 @@ mod solve_expr {
|
|||
Empty
|
||||
|
||||
|
||||
main : Dict Int
|
||||
main : Dict I64
|
||||
main =
|
||||
balance 0 Empty
|
||||
"#
|
||||
),
|
||||
"Dict Int",
|
||||
"Dict I64",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1124,7 +1124,7 @@ mod solve_uniq_expr {
|
|||
x
|
||||
"#
|
||||
),
|
||||
"Attr * Int",
|
||||
"Attr * I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1377,7 +1377,7 @@ mod solve_uniq_expr {
|
|||
x
|
||||
"#
|
||||
),
|
||||
"Attr * Int",
|
||||
"Attr * I64",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1405,7 +1405,7 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -1418,7 +1418,7 @@ mod solve_uniq_expr {
|
|||
swap
|
||||
"#
|
||||
),
|
||||
"Attr * (Attr * Int, Attr * Int, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))"
|
||||
"Attr * (Attr * I64, Attr * I64, Attr * (List (Attr Shared a)) -> Attr * (List (Attr Shared a)))"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1431,7 +1431,7 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
quicksort : List (Num a), Int, Int -> List (Num a)
|
||||
quicksort : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksort = \list, low, high ->
|
||||
when partition low high list is
|
||||
Pair partitionIndex partitioned ->
|
||||
|
@ -1440,7 +1440,7 @@ mod solve_uniq_expr {
|
|||
|> quicksort (partitionIndex + 1) high
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -1452,7 +1452,7 @@ mod solve_uniq_expr {
|
|||
[]
|
||||
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -1480,7 +1480,7 @@ mod solve_uniq_expr {
|
|||
quicksort
|
||||
"#
|
||||
),
|
||||
"Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared Int, Attr Shared Int -> Attr b (List (Attr Shared (Num (Attr Shared a)))))"
|
||||
"Attr Shared (Attr b (List (Attr Shared (Num (Attr Shared a)))), Attr Shared I64, Attr Shared I64 -> Attr b (List (Attr Shared (Num (Attr Shared a)))))"
|
||||
);
|
||||
})
|
||||
}
|
||||
|
@ -2149,7 +2149,7 @@ mod solve_uniq_expr {
|
|||
Num.maxInt // Num.maxInt
|
||||
"#
|
||||
),
|
||||
"Attr * (Result (Attr * Int) (Attr * [ DivByZero ]*))",
|
||||
"Attr * (Result (Attr * I64) (Attr * [ DivByZero ]*))",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2161,7 +2161,7 @@ mod solve_uniq_expr {
|
|||
3 // 4
|
||||
"#
|
||||
),
|
||||
"Attr * (Result (Attr * Int) (Attr * [ DivByZero ]*))",
|
||||
"Attr * (Result (Attr * I64) (Attr * [ DivByZero ]*))",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2173,7 +2173,7 @@ mod solve_uniq_expr {
|
|||
3 // Num.maxInt
|
||||
"#
|
||||
),
|
||||
"Attr * (Result (Attr * Int) (Attr * [ DivByZero ]*))",
|
||||
"Attr * (Result (Attr * I64) (Attr * [ DivByZero ]*))",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2264,17 +2264,17 @@ mod solve_uniq_expr {
|
|||
|
||||
#[test]
|
||||
fn list_len() {
|
||||
infer_eq("List.len", "Attr * (Attr * (List *) -> Attr * Int)");
|
||||
infer_eq("List.len", "Attr * (Attr * (List *) -> Attr * I64)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_get() {
|
||||
infer_eq("List.get", "Attr * (Attr (* | b) (List (Attr b a)), Attr * Int -> Attr * (Result (Attr b a) (Attr * [ OutOfBounds ]*)))");
|
||||
infer_eq("List.get", "Attr * (Attr (* | b) (List (Attr b a)), Attr * I64 -> Attr * (Result (Attr b a) (Attr * [ OutOfBounds ]*)))");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_set() {
|
||||
infer_eq("List.set", "Attr * (Attr (* | b | c) (List (Attr b a)), Attr * Int, Attr (b | c) a -> Attr * (List (Attr b a)))");
|
||||
infer_eq("List.set", "Attr * (Attr (* | b | c) (List (Attr b a)), Attr * I64, Attr (b | c) a -> Attr * (List (Attr b a)))");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2286,7 +2286,7 @@ mod solve_uniq_expr {
|
|||
fn list_repeat() {
|
||||
infer_eq(
|
||||
"List.repeat",
|
||||
"Attr * (Attr * Int, Attr Shared a -> Attr * (List (Attr Shared a)))",
|
||||
"Attr * (Attr * I64, Attr Shared a -> Attr * (List (Attr Shared a)))",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2487,15 +2487,15 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
Model : { foo : Int }
|
||||
Model : { foo : I64 }
|
||||
|
||||
extract : Model -> Int
|
||||
extract : Model -> I64
|
||||
extract = \{ foo } -> foo
|
||||
|
||||
extract
|
||||
"#
|
||||
),
|
||||
"Attr * (Attr (* | a) Model -> Attr a Int)",
|
||||
"Attr * (Attr (* | a) Model -> Attr a I64)",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2504,15 +2504,15 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
Model : { foo : Int, bar : Int }
|
||||
Model : { foo : I64, bar : I64 }
|
||||
|
||||
extract : Model -> Int
|
||||
extract : Model -> I64
|
||||
extract = \{ foo } -> foo
|
||||
|
||||
extract
|
||||
"#
|
||||
),
|
||||
"Attr * (Attr (* | a) Model -> Attr a Int)",
|
||||
"Attr * (Attr (* | a) Model -> Attr a I64)",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2521,15 +2521,15 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
Model : { foo : Int, bar : Int }
|
||||
Model : { foo : I64, bar : I64 }
|
||||
|
||||
extract : Model -> Int
|
||||
extract : Model -> I64
|
||||
extract = \{foo, bar} -> foo + bar
|
||||
|
||||
extract
|
||||
"#
|
||||
),
|
||||
"Attr * (Attr (* | * | *) Model -> Attr * Int)",
|
||||
"Attr * (Attr (* | * | *) Model -> Attr * I64)",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2627,13 +2627,13 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
f : Int, Int -> Int
|
||||
f : I64, I64 -> I64
|
||||
f = \a, b -> a + b
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
"Attr * (Attr * Int, Attr * Int -> Attr * Int)",
|
||||
"Attr * (Attr * I64, Attr * I64 -> Attr * I64)",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2642,13 +2642,13 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
foobar : Int -> Int
|
||||
foobar : I64 -> I64
|
||||
foobar = \x -> Num.abs x
|
||||
|
||||
foobar
|
||||
"#
|
||||
),
|
||||
"Attr * (Attr * Int -> Attr * Int)",
|
||||
"Attr * (Attr * I64 -> Attr * I64)",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2662,7 +2662,7 @@ mod solve_uniq_expr {
|
|||
f
|
||||
"#
|
||||
),
|
||||
"Attr * (Attr a Int, Attr b Int -> Attr c Int)",
|
||||
"Attr * (Attr a I64, Attr b I64 -> Attr c I64)",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3001,13 +3001,13 @@ mod solve_uniq_expr {
|
|||
Model a : { foo : Set a }
|
||||
|
||||
|
||||
initialModel : position -> Model Int
|
||||
initialModel : position -> Model I64
|
||||
initialModel = \_ -> { foo : Set.empty }
|
||||
|
||||
initialModel
|
||||
"#
|
||||
),
|
||||
"Attr * (Attr * position -> Attr * (Model (Attr * Int)))",
|
||||
"Attr * (Attr * position -> Attr * (Model (Attr * I64)))",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3035,12 +3035,12 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
negatePoint : { x : Int, y : Int, z ? Num c } -> { x : Int, y : Int, z : Num c }
|
||||
negatePoint : { x : I64, y : I64, z ? Num c } -> { x : I64, y : I64, z : Num c }
|
||||
|
||||
negatePoint { x: 1, y: 2 }
|
||||
"#
|
||||
),
|
||||
"Attr * { x : Attr * Int, y : Attr * Int, z : Attr * (Num (Attr * c)) }",
|
||||
"Attr * { x : Attr * I64, y : Attr * I64, z : Attr * (Num (Attr * c)) }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3049,7 +3049,7 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
negatePoint : { x : Int, y : Int, z ? Num c }r -> { x : Int, y : Int, z : Num c }r
|
||||
negatePoint : { x : I64, y : I64, z ? Num c }r -> { x : I64, y : I64, z : Num c }r
|
||||
|
||||
a = negatePoint { x: 1, y: 2 }
|
||||
b = negatePoint { x: 1, y: 2, blah : "hi" }
|
||||
|
@ -3057,7 +3057,7 @@ mod solve_uniq_expr {
|
|||
{ a, b }
|
||||
"#
|
||||
),
|
||||
"Attr * { a : Attr * { x : Attr * Int, y : Attr * Int, z : Attr * (Num (Attr * c)) }, b : Attr * { blah : Attr * Str, x : Attr * Int, y : Attr * Int, z : Attr * (Num (Attr * c)) } }"
|
||||
"Attr * { a : Attr * { x : Attr * I64, y : Attr * I64, z : Attr * (Num (Attr * c)) }, b : Attr * { blah : Attr * Str, x : Attr * I64, y : Attr * I64, z : Attr * (Num (Attr * c)) } }"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3071,7 +3071,7 @@ mod solve_uniq_expr {
|
|||
negatePoint { x: 1, y: 2.1, z: 0x3 }
|
||||
"#
|
||||
),
|
||||
"Attr * { x : Attr * (Num (Attr * a)), y : Attr * F64, z : Attr * Int }",
|
||||
"Attr * { x : Attr * (Num (Attr * a)), y : Attr * F64, z : Attr * I64 }",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3149,14 +3149,14 @@ mod solve_uniq_expr {
|
|||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
empty : List Int
|
||||
empty : List I64
|
||||
empty =
|
||||
[]
|
||||
|
||||
List.walkBackwards empty (\a, b -> a + b) 0
|
||||
"#
|
||||
),
|
||||
"Attr a Int",
|
||||
"Attr a I64",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
|||
|
||||
// Int : Num Integer
|
||||
add_alias(
|
||||
Symbol::NUM_INT,
|
||||
Symbol::NUM_I64,
|
||||
BuiltinAlias {
|
||||
region: Region::zero(),
|
||||
vars: Vec::new(),
|
||||
|
@ -155,7 +155,7 @@ fn float_alias_content() -> SolvedType {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn int_type() -> SolvedType {
|
||||
SolvedType::Alias(Symbol::NUM_INT, Vec::new(), Box::new(int_alias_content()))
|
||||
SolvedType::Alias(Symbol::NUM_I64, Vec::new(), Box::new(int_alias_content()))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -19,8 +19,8 @@ static EMPTY_TAG_UNION: &str = "[]";
|
|||
///
|
||||
/// Separately, if we're inside a type parameter, we may need to use parens:
|
||||
///
|
||||
/// List Int
|
||||
/// List (List Int)
|
||||
/// List I64
|
||||
/// List (List I64)
|
||||
///
|
||||
/// Otherwise, parens are unnecessary.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
|
@ -330,7 +330,7 @@ fn write_content(env: &Env, content: Content, subs: &Subs, buf: &mut String, par
|
|||
|
||||
match &content {
|
||||
Alias(nested, _, _) => match *nested {
|
||||
Symbol::NUM_INTEGER => buf.push_str("Int"),
|
||||
Symbol::NUM_INTEGER => buf.push_str("I64"),
|
||||
Symbol::NUM_FLOATINGPOINT => buf.push_str("F64"),
|
||||
|
||||
_ => write_parens!(write_parens, buf, {
|
||||
|
@ -343,7 +343,7 @@ fn write_content(env: &Env, content: Content, subs: &Subs, buf: &mut String, par
|
|||
let attr_content = subs.get_without_compacting(nested_args[1]).content;
|
||||
match &attr_content {
|
||||
Alias(nested, _, _) => match *nested {
|
||||
Symbol::NUM_INTEGER => buf.push_str("Int"),
|
||||
Symbol::NUM_INTEGER => buf.push_str("I64"),
|
||||
Symbol::NUM_FLOATINGPOINT => buf.push_str("F64"),
|
||||
_ => write_parens!(write_parens, buf, {
|
||||
buf.push_str("Num ");
|
||||
|
@ -403,7 +403,7 @@ fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String
|
|||
Record(fields, ext_var) => {
|
||||
use crate::types::{gather_fields, RecordStructure};
|
||||
|
||||
// If the `ext` has concrete fields (e.g. { foo : Int}{ bar : Bool }), merge them
|
||||
// If the `ext` has concrete fields (e.g. { foo : I64}{ bar : Bool }), merge them
|
||||
let RecordStructure { fields, ext } = gather_fields(subs, fields, ext_var);
|
||||
let ext_var = ext;
|
||||
|
||||
|
@ -465,8 +465,8 @@ fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String
|
|||
// This is an open record, so print the variable
|
||||
// right after the '}'
|
||||
//
|
||||
// e.g. the "*" at the end of `{ x: Int }*`
|
||||
// or the "r" at the end of `{ x: Int }r`
|
||||
// e.g. the "*" at the end of `{ x: I64 }*`
|
||||
// or the "r" at the end of `{ x: I64 }r`
|
||||
write_content(env, content, subs, buf, parens)
|
||||
}
|
||||
}
|
||||
|
@ -523,8 +523,8 @@ fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String
|
|||
// This is an open tag union, so print the variable
|
||||
// right after the ']'
|
||||
//
|
||||
// e.g. the "*" at the end of `{ x: Int }*`
|
||||
// or the "r" at the end of `{ x: Int }r`
|
||||
// e.g. the "*" at the end of `{ x: I64 }*`
|
||||
// or the "r" at the end of `{ x: I64 }r`
|
||||
write_content(env, content, subs, buf, parens)
|
||||
}
|
||||
}
|
||||
|
@ -576,8 +576,8 @@ fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String
|
|||
// This is an open tag union, so print the variable
|
||||
// right after the ']'
|
||||
//
|
||||
// e.g. the "*" at the end of `{ x: Int }*`
|
||||
// or the "r" at the end of `{ x: Int }r`
|
||||
// e.g. the "*" at the end of `{ x: I64 }*`
|
||||
// or the "r" at the end of `{ x: I64 }r`
|
||||
write_content(env, content, subs, buf, parens)
|
||||
}
|
||||
|
||||
|
@ -754,7 +754,7 @@ fn write_apply(
|
|||
match &arg_content {
|
||||
Content::Structure(FlatType::Apply(symbol, nested_args)) => match *symbol {
|
||||
Symbol::NUM_INTEGER if nested_args.is_empty() => {
|
||||
buf.push_str("Int");
|
||||
buf.push_str("I64");
|
||||
}
|
||||
Symbol::NUM_FLOATINGPOINT if nested_args.is_empty() => {
|
||||
buf.push_str("F64");
|
||||
|
@ -768,7 +768,7 @@ fn write_apply(
|
|||
double_nested_args,
|
||||
))) => match double_nested_symbol {
|
||||
Symbol::NUM_INTEGER if double_nested_args.is_empty() => {
|
||||
buf.push_str("Int");
|
||||
buf.push_str("I64");
|
||||
}
|
||||
Symbol::NUM_FLOATINGPOINT if double_nested_args.is_empty() => {
|
||||
buf.push_str("F64");
|
||||
|
|
|
@ -14,7 +14,7 @@ use roc_types::types::Type::{self, *};
|
|||
pub fn int_literal(num_var: Variable, expected: Expected<Type>, region: Region) -> Constraint {
|
||||
let num_type = Variable(num_var);
|
||||
let reason = Reason::IntLiteral;
|
||||
let int_type = builtin_type(Symbol::NUM_INT, vec![]);
|
||||
let int_type = builtin_type(Symbol::NUM_I64, vec![]);
|
||||
let expected_literal = ForReason(reason, int_type, region);
|
||||
|
||||
exists(
|
||||
|
|
|
@ -23,13 +23,17 @@ roc_solve = { path = "../compiler/solve" }
|
|||
roc_mono = { path = "../compiler/mono" }
|
||||
roc_load = { path = "../compiler/load" }
|
||||
roc_gen = { path = "../compiler/gen" }
|
||||
roc_fmt = { path = "../compiler/fmt" }
|
||||
|
||||
roc_reporting = { path = "../compiler/reporting" }
|
||||
# TODO switch to clap 3.0.0 once it's out. Tried adding clap = "~3.0.0-beta.1" and cargo wouldn't accept it
|
||||
im = "14" # im and im-rc should always have the same version!
|
||||
im-rc = "14" # im and im-rc should always have the same version!
|
||||
bumpalo = { version = "3.2", features = ["collections"] }
|
||||
inlinable_string = "0.1"
|
||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded", "process", "io-driver"] }
|
||||
arraystring = "0.3.0"
|
||||
libc = "0.2"
|
||||
page_size = "0.4"
|
||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
||||
#
|
||||
# The reason for this fork is that the way Inkwell is designed, you have to use
|
||||
|
@ -57,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"
|
||||
|
|
1029
editor/src/ast.rs
1029
editor/src/ast.rs
File diff suppressed because it is too large
Load diff
457
editor/src/bucket.rs
Normal file
457
editor/src/bucket.rs
Normal file
|
@ -0,0 +1,457 @@
|
|||
/// A bucket of 16-byte nodes. The node value 0 is reserved for the bucket's
|
||||
/// use, and valid nodes may never have that value.
|
||||
///
|
||||
/// By design, each bucket is 4096 bytes large. When you make a bucket, it
|
||||
/// uses mmap to reserve one anonymous memory page in which to store nodes.
|
||||
/// Since nodes are 16 bytes, one bucket can store 256 nodes; you can access
|
||||
/// a particular node by its BucketSlot, which is an opaque wrapper around a u8.
|
||||
///
|
||||
/// Buckets also use the node value 0 (all 0 bits) to mark slots as unoccupied.
|
||||
/// This is important for performance.
|
||||
use libc::{c_void, calloc, free, mmap, munmap, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::size_of;
|
||||
use std::ptr::null;
|
||||
|
||||
pub const BUCKET_BYTES: usize = 4096;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NodeId<T: Sized> {
|
||||
pub bucket_id: BucketId<T>,
|
||||
pub slot: BucketSlot<T>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_of_node_id() {
|
||||
assert_eq!(std::mem::size_of::<NodeId<()>>(), 4);
|
||||
}
|
||||
|
||||
impl<T> Clone for NodeId<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for NodeId<T> {}
|
||||
|
||||
impl<T: Sized> NodeId<T> {
|
||||
fn next_slot(&self) -> Self {
|
||||
NodeId {
|
||||
bucket_id: self.bucket_id,
|
||||
slot: self.slot.increment(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for NodeId<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.bucket_id == other.bucket_id && self.slot == other.slot
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for NodeId<T> {}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct BucketId<T: Sized> {
|
||||
value: u16,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_of_bucket_id() {
|
||||
assert_eq!(std::mem::size_of::<BucketId<()>>(), 2);
|
||||
}
|
||||
|
||||
impl<T> Clone for BucketId<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for BucketId<T> {}
|
||||
|
||||
impl<T> PartialEq for BucketId<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.value == other.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for BucketId<T> {}
|
||||
|
||||
impl<T: Sized> BucketId<T> {
|
||||
fn from_u16(value: u16) -> Self {
|
||||
BucketId {
|
||||
value,
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct BucketSlot<T: Sized> {
|
||||
value: u8,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_of_bucket_slot() {
|
||||
assert_eq!(std::mem::size_of::<BucketSlot<()>>(), 1);
|
||||
}
|
||||
|
||||
impl<T> Clone for BucketSlot<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for BucketSlot<T> {}
|
||||
|
||||
impl<T> PartialEq for BucketSlot<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.value == other.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for BucketSlot<T> {}
|
||||
|
||||
impl<T: Sized> BucketSlot<T> {
|
||||
#[allow(dead_code)]
|
||||
fn from_u8(value: u8) -> Self {
|
||||
BucketSlot {
|
||||
value,
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn increment(&self) -> Self {
|
||||
BucketSlot {
|
||||
value: self.value + 1,
|
||||
_phantom: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Buckets {
|
||||
buckets: Vec<Bucket>,
|
||||
// free_1node_slots: Vec<NodeId<T>>,
|
||||
}
|
||||
|
||||
impl Buckets {
|
||||
// fn find_space_for(&mut self, nodes: u8) -> Result<BucketId<T>, ()> {}
|
||||
|
||||
pub fn add<T: Sized>(&mut self) -> Result<BucketId<T>, ()> {
|
||||
let num_buckets = self.buckets.len();
|
||||
|
||||
if num_buckets <= u16::MAX as usize {
|
||||
let bucket_id = BucketId::from_u16(num_buckets as u16);
|
||||
let bucket = Bucket::default();
|
||||
|
||||
self.buckets.push(bucket);
|
||||
|
||||
Ok(bucket_id)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_unchecked<'a, T: Sized>(&'a self, node_id: NodeId<T>) -> &'a T {
|
||||
unsafe {
|
||||
self.buckets
|
||||
.get(node_id.bucket_id.value as usize)
|
||||
.unwrap()
|
||||
.get_unchecked(node_id.slot.value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<'a, T: Sized>(&'a self, node_id: NodeId<T>) -> Option<&'a T> {
|
||||
self.buckets
|
||||
.get(node_id.bucket_id.value as usize)
|
||||
.and_then(|bucket| bucket.get(node_id.slot))
|
||||
}
|
||||
}
|
||||
|
||||
struct Bucket {
|
||||
#[allow(dead_code)]
|
||||
next_unused_slot: u16,
|
||||
first_slot: *mut [u8; 16],
|
||||
}
|
||||
|
||||
impl Bucket {
|
||||
/// If there's room left in the bucket, adds the item and returns
|
||||
/// the slot where it was put. If there was no room left, returns Err(()).
|
||||
#[allow(dead_code)]
|
||||
pub fn add<T: Sized>(&mut self, node: T) -> Result<BucketSlot<T>, ()> {
|
||||
// It's only safe to store this as a *const T if T is 16 bytes.
|
||||
// This is designed to be used exclusively with 16-byte nodes!
|
||||
debug_assert_eq!(size_of::<T>(), 16);
|
||||
|
||||
// Once next_unused_slot exceeds u8::MAX, we have no room left.
|
||||
if self.next_unused_slot <= u8::MAX as u16 {
|
||||
let chosen_slot = self.next_unused_slot as u8;
|
||||
|
||||
unsafe { self.put_unchecked(node, chosen_slot) };
|
||||
self.next_unused_slot += 1;
|
||||
|
||||
Ok(BucketSlot::from_u8(chosen_slot))
|
||||
} else {
|
||||
// No room left!
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// If the given slot is available, inserts the given node into it.
|
||||
/// Otherwise, returns the node that was in the already-occupied slot.
|
||||
#[allow(dead_code)]
|
||||
pub fn insert<T: Sized>(&mut self, node: T, slot: BucketSlot<T>) -> Result<(), &T> {
|
||||
// It's only safe to use this if T is 16 bytes.
|
||||
// This is designed to be used exclusively with 16-byte nodes!
|
||||
debug_assert_eq!(size_of::<T>(), 16);
|
||||
|
||||
let slot = slot.value;
|
||||
|
||||
unsafe {
|
||||
if self.is_available(slot) {
|
||||
self.put_unchecked(node, slot);
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(self.get_unchecked(slot))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<'a, T: Sized>(&'a self, slot: BucketSlot<T>) -> Option<&'a T> {
|
||||
// It's only safe to store this as a *const T if T is 16 bytes.
|
||||
// This is designed to be used exclusively with 16-byte nodes!
|
||||
debug_assert_eq!(size_of::<T>(), 16);
|
||||
|
||||
unsafe {
|
||||
let slot_ptr = self.first_slot.offset(slot.value as isize) as *const T;
|
||||
let value: &[u8; 16] = &*(slot_ptr as *const [u8; 16]);
|
||||
|
||||
if *value != [0; 16] {
|
||||
Some(&*(value as *const [u8; 16] as *const T))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn put_unchecked<T: Sized>(&mut self, node: T, slot: u8) {
|
||||
// It's only safe to store this as a *const T if T is 16 bytes.
|
||||
// This is designed to be used exclusively with 16-byte nodes!
|
||||
debug_assert_eq!(size_of::<T>(), 16);
|
||||
|
||||
let slot_ptr = self.first_slot.offset(slot as isize) as *mut T;
|
||||
|
||||
*slot_ptr = node;
|
||||
}
|
||||
|
||||
unsafe fn get_unchecked<T>(&self, slot: u8) -> &T {
|
||||
&*(self.first_slot.offset(slot as isize) as *const T)
|
||||
}
|
||||
|
||||
// A slot is available iff its bytes are all zeroes
|
||||
unsafe fn is_available(&self, slot: u8) -> bool {
|
||||
let slot_ptr = self.first_slot.offset(slot as isize) as *const [u8; 16];
|
||||
|
||||
*slot_ptr == [0; 16]
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Bucket {
|
||||
fn default() -> Self {
|
||||
let first_slot = if page_size::get() == 4096 {
|
||||
unsafe {
|
||||
// mmap exactly one memory page (4096 bytes)
|
||||
mmap(
|
||||
null::<c_void>() as *mut c_void,
|
||||
BUCKET_BYTES,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Somehow the page size is not 4096 bytes, so fall back on calloc.
|
||||
// (We use calloc over malloc because we rely on the bytes having
|
||||
// been zeroed to tell which slots are available.)
|
||||
unsafe { calloc(1, BUCKET_BYTES) }
|
||||
} as *mut [u8; 16];
|
||||
|
||||
Bucket {
|
||||
next_unused_slot: 0,
|
||||
first_slot,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Bucket {
|
||||
fn drop(&mut self) {
|
||||
if page_size::get() == 4096 {
|
||||
unsafe {
|
||||
munmap(self.first_slot as *mut c_void, BUCKET_BYTES);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
free(self.first_slot as *mut c_void);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BucketStr {
|
||||
first_node_id: NodeId<()>,
|
||||
first_segment_len: u8,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_of_bucket_str() {
|
||||
assert_eq!(std::mem::size_of::<BucketList<()>>(), 4);
|
||||
}
|
||||
|
||||
/// A non-empty list inside a bucket. It takes 4B of memory.
|
||||
///
|
||||
/// This is internally represented as an array of at most 255 nodes, which
|
||||
/// can grow to 256+ nodes by having the last nodeent be a linked list Cons
|
||||
/// cell which points to another such backing array which has more nodes.
|
||||
///
|
||||
/// In practice, these will almost be far below 256 nodes, but in theory
|
||||
/// they can be enormous in length thanks to the linked list fallback.
|
||||
///
|
||||
/// Since these are non-empty lists, we need separate variants for collections
|
||||
/// that can be empty, e.g. EmptyRecord and EmptyList. In contrast, we don't
|
||||
/// need an EmptyList or EmptyWhen, since although those use BucketList
|
||||
/// to store their branches, having zero branches is syntactically invalid.
|
||||
/// Same with Call and Closure, since all functions must have 1+ arguments.
|
||||
#[derive(Debug)]
|
||||
pub struct BucketList<T: Sized> {
|
||||
first_node_id: BucketId<T>,
|
||||
first_node_sl: BucketSlot<T>,
|
||||
first_segment_len: u8,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_of_bucket_list() {
|
||||
assert_eq!(std::mem::size_of::<BucketList<()>>(), 4);
|
||||
}
|
||||
|
||||
impl<'a, T: 'a + Sized> BucketList<T> {
|
||||
/// If given a first_segment_len of 0, that means this is a BucketList
|
||||
/// consisting of 256+ nodes. The first 255 are stored in the usual
|
||||
/// array, and then there's one more nodeent at the end which continues
|
||||
/// the list with a new length and NodeId value. BucketList iterators
|
||||
/// automatically do these jumps behind the scenes when necessary.
|
||||
pub fn new(first_node_id: NodeId<T>, first_segment_len: u8) -> Self {
|
||||
BucketList {
|
||||
first_segment_len,
|
||||
first_node_id: first_node_id.bucket_id,
|
||||
first_node_sl: first_node_id.slot,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_iter(self, buckets: &'a Buckets) -> impl Iterator<Item = &'a T> {
|
||||
self.bucket_list_iter(buckets)
|
||||
}
|
||||
|
||||
/// Private version of into_iter which exposes the implementation detail
|
||||
/// of BucketListIter. We don't want that struct to be public, but we
|
||||
/// actually do want to have this separate function for code reuse
|
||||
/// in the iterator's next() method.
|
||||
fn bucket_list_iter(&self, buckets: &'a Buckets) -> BucketListIter<'a, T> {
|
||||
let first_segment_len = self.first_segment_len;
|
||||
let continues_with_cons = first_segment_len == 0;
|
||||
let len_remaining = if continues_with_cons {
|
||||
// We have 255 nodes followed by a Cons cell continuing the list.
|
||||
u8::MAX
|
||||
} else {
|
||||
first_segment_len
|
||||
};
|
||||
|
||||
BucketListIter {
|
||||
continues_with_cons,
|
||||
len_remaining,
|
||||
bucket_id: self.first_node_id,
|
||||
slot: self.first_node_sl,
|
||||
buckets,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BucketListIter<'a, T: Sized> {
|
||||
bucket_id: BucketId<T>,
|
||||
slot: BucketSlot<T>,
|
||||
len_remaining: u8,
|
||||
continues_with_cons: bool,
|
||||
buckets: &'a Buckets,
|
||||
}
|
||||
|
||||
impl<'a, T: Sized> Iterator for BucketListIter<'a, T>
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.len_remaining {
|
||||
0 => match self.continues_with_cons {
|
||||
// We're done! This is by far the most common case, so we put
|
||||
// it first to avoid branch mispredictions.
|
||||
false => None,
|
||||
// We need to continue with a Cons cell.
|
||||
true => {
|
||||
let node_id = NodeId {
|
||||
bucket_id: self.bucket_id,
|
||||
slot: self.slot,
|
||||
}
|
||||
.next_slot();
|
||||
|
||||
// Since we have continues_with_cons set, the next slot
|
||||
// will definitely be occupied with a BucketList struct.
|
||||
let node = self.buckets.get_unchecked(node_id);
|
||||
let next_list = unsafe { &*(node as *const T as *const BucketList<T>) };
|
||||
|
||||
// Replace the current iterator with an iterator into that
|
||||
// list, and then continue with next() on that iterator.
|
||||
let next_iter = next_list.bucket_list_iter(self.buckets);
|
||||
|
||||
self.bucket_id = next_iter.bucket_id;
|
||||
self.slot = next_iter.slot;
|
||||
self.len_remaining = next_iter.len_remaining;
|
||||
self.continues_with_cons = next_iter.continues_with_cons;
|
||||
|
||||
self.next()
|
||||
}
|
||||
},
|
||||
1 => {
|
||||
self.len_remaining = 0;
|
||||
|
||||
// Don't advance the node pointer's slot, because that might
|
||||
// advance past the end of the bucket!
|
||||
|
||||
Some(self.buckets.get_unchecked(NodeId {
|
||||
bucket_id: self.bucket_id,
|
||||
slot: self.slot,
|
||||
}))
|
||||
}
|
||||
len_remaining => {
|
||||
// Get the current node
|
||||
let node_id = NodeId {
|
||||
bucket_id: self.bucket_id,
|
||||
slot: self.slot,
|
||||
};
|
||||
let node = self.buckets.get_unchecked(node_id);
|
||||
|
||||
// Advance the node pointer to the next slot in the current bucket
|
||||
self.slot = self.slot.increment();
|
||||
self.len_remaining = len_remaining - 1;
|
||||
|
||||
Some(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
166
editor/src/buffer.rs
Normal file
166
editor/src/buffer.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
// 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 RectBuffers {
|
||||
pub vertex_buffer: wgpu::Buffer,
|
||||
pub index_buffer: wgpu::Buffer,
|
||||
pub num_rects: u32,
|
||||
}
|
||||
|
||||
pub 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: [1.0, 0.0, 0.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,
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
97
editor/src/file.rs
Normal file
97
editor/src/file.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_fmt::def::fmt_def;
|
||||
use roc_fmt::module::fmt_module;
|
||||
use roc_parse::ast::{Attempting, Def, Module};
|
||||
use roc_parse::module::module_defs;
|
||||
use roc_parse::parser;
|
||||
use roc_parse::parser::Parser;
|
||||
use roc_region::all::Located;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct File<'a> {
|
||||
path: &'a Path,
|
||||
module_header: Module<'a>,
|
||||
content: Vec<'a, Located<Def<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ReadError {
|
||||
Read(std::io::Error),
|
||||
ParseDefs(parser::Fail),
|
||||
ParseHeader(parser::Fail),
|
||||
DoesntHaveRocExtension,
|
||||
}
|
||||
|
||||
impl<'a> File<'a> {
|
||||
pub fn read(path: &'a Path, arena: &'a Bump) -> Result<File<'a>, ReadError> {
|
||||
if path.extension() != Some(OsStr::new("roc")) {
|
||||
return Err(ReadError::DoesntHaveRocExtension);
|
||||
}
|
||||
|
||||
let bytes = fs::read(path).map_err(ReadError::Read)?;
|
||||
|
||||
let allocation = arena.alloc(bytes);
|
||||
|
||||
let module_parse_state = parser::State::new(allocation, Attempting::Module);
|
||||
let parsed_module = roc_parse::module::header().parse(&arena, module_parse_state);
|
||||
|
||||
match parsed_module {
|
||||
Ok((module, state)) => {
|
||||
let parsed_defs = module_defs().parse(&arena, state);
|
||||
|
||||
match parsed_defs {
|
||||
Ok((defs, _)) => Ok(File {
|
||||
path,
|
||||
module_header: module,
|
||||
content: defs,
|
||||
}),
|
||||
Err((error, _)) => Err(ReadError::ParseDefs(error)),
|
||||
}
|
||||
}
|
||||
Err((error, _)) => Err(ReadError::ParseHeader(error)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmt(&self) -> String {
|
||||
let arena = Bump::new();
|
||||
let mut formatted_file = String::new();
|
||||
|
||||
let mut module_header_buf = bumpalo::collections::String::new_in(&arena);
|
||||
fmt_module(&mut module_header_buf, &self.module_header);
|
||||
|
||||
formatted_file.push_str(module_header_buf.as_str());
|
||||
|
||||
for def in &self.content {
|
||||
let mut def_buf = bumpalo::collections::String::new_in(&arena);
|
||||
|
||||
fmt_def(&mut def_buf, &def.value, 0);
|
||||
|
||||
formatted_file.push_str(def_buf.as_str());
|
||||
}
|
||||
|
||||
formatted_file
|
||||
}
|
||||
|
||||
pub fn fmt_then_write_to(&self, write_path: &'a Path) -> io::Result<()> {
|
||||
let formatted_file = self.fmt();
|
||||
|
||||
fs::write(write_path, formatted_file)
|
||||
}
|
||||
|
||||
pub fn fmt_then_write_with_name(&self, new_name: &str) -> io::Result<()> {
|
||||
self.fmt_then_write_to(
|
||||
self.path
|
||||
.with_file_name(new_name)
|
||||
.with_extension("roc")
|
||||
.as_path(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fmt_then_write(&self) -> io::Result<()> {
|
||||
self.fmt_then_write_to(self.path)
|
||||
}
|
||||
}
|
153
editor/src/keyboard_input.rs
Normal file
153
editor/src/keyboard_input.rs
Normal file
|
@ -0,0 +1,153 @@
|
|||
use winit::event::{ElementState, ModifiersState, VirtualKeyCode};
|
||||
|
||||
pub fn handle_keydown(
|
||||
elem_state: ElementState,
|
||||
virtual_keycode: VirtualKeyCode,
|
||||
_modifiers: ModifiersState,
|
||||
) {
|
||||
use winit::event::VirtualKeyCode::*;
|
||||
|
||||
if let ElementState::Released = elem_state {
|
||||
return;
|
||||
}
|
||||
|
||||
match virtual_keycode {
|
||||
Copy => {
|
||||
todo!("copy");
|
||||
}
|
||||
Paste => {
|
||||
todo!("paste");
|
||||
}
|
||||
Cut => {
|
||||
todo!("cut");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn handle_text_input(
|
||||
// text_state: &mut String,
|
||||
// elem_state: ElementState,
|
||||
// virtual_keycode: VirtualKeyCode,
|
||||
// _modifiers: ModifiersState,
|
||||
// ) {
|
||||
// use winit::event::VirtualKeyCode::*;
|
||||
|
||||
// if let ElementState::Released = elem_state {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// match virtual_keycode {
|
||||
// Key1 | Numpad1 => text_state.push('1'),
|
||||
// Key2 | Numpad2 => text_state.push('2'),
|
||||
// Key3 | Numpad3 => text_state.push('3'),
|
||||
// Key4 | Numpad4 => text_state.push('4'),
|
||||
// Key5 | Numpad5 => text_state.push('5'),
|
||||
// Key6 | Numpad6 => text_state.push('6'),
|
||||
// Key7 | Numpad7 => text_state.push('7'),
|
||||
// Key8 | Numpad8 => text_state.push('8'),
|
||||
// Key9 | Numpad9 => text_state.push('9'),
|
||||
// Key0 | Numpad0 => text_state.push('0'),
|
||||
// A => text_state.push('a'),
|
||||
// B => text_state.push('b'),
|
||||
// C => text_state.push('c'),
|
||||
// D => text_state.push('d'),
|
||||
// E => text_state.push('e'),
|
||||
// F => text_state.push('f'),
|
||||
// G => text_state.push('g'),
|
||||
// H => text_state.push('h'),
|
||||
// I => text_state.push('i'),
|
||||
// J => text_state.push('j'),
|
||||
// K => text_state.push('k'),
|
||||
// L => text_state.push('l'),
|
||||
// M => text_state.push('m'),
|
||||
// N => text_state.push('n'),
|
||||
// O => text_state.push('o'),
|
||||
// P => text_state.push('p'),
|
||||
// Q => text_state.push('q'),
|
||||
// R => text_state.push('r'),
|
||||
// S => text_state.push('s'),
|
||||
// T => text_state.push('t'),
|
||||
// U => text_state.push('u'),
|
||||
// V => text_state.push('v'),
|
||||
// W => text_state.push('w'),
|
||||
// X => text_state.push('x'),
|
||||
// Y => text_state.push('y'),
|
||||
// Z => text_state.push('z'),
|
||||
// Escape | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | F14 | F15
|
||||
// | F16 | F17 | F18 | F19 | F20 | F21 | F22 | F23 | F24 | Snapshot | Scroll | Pause
|
||||
// | Insert | Home | Delete | End | PageDown | PageUp | Left | Up | Right | Down | Compose
|
||||
// | Caret | Numlock | AbntC1 | AbntC2 | Ax | Calculator | Capital | Convert | Kana
|
||||
// | Kanji | LAlt | LBracket | LControl | LShift | LWin | Mail | MediaSelect | PlayPause
|
||||
// | Power | PrevTrack | MediaStop | Mute | MyComputer | NavigateForward
|
||||
// | NavigateBackward | NextTrack | NoConvert | OEM102 | RAlt | Sysrq | RBracket
|
||||
// | RControl | RShift | RWin | Sleep | Stop | Unlabeled | VolumeDown | VolumeUp | Wake
|
||||
// | WebBack | WebFavorites | WebForward | WebHome | WebRefresh | WebSearch | Apps | Tab
|
||||
// | WebStop => {
|
||||
// // TODO handle
|
||||
// }
|
||||
// Back => {
|
||||
// text_state.pop();
|
||||
// }
|
||||
// Return | NumpadEnter => {
|
||||
// text_state.push('\n');
|
||||
// }
|
||||
// Space => {
|
||||
// text_state.push(' ');
|
||||
// }
|
||||
// Comma | NumpadComma => {
|
||||
// text_state.push(',');
|
||||
// }
|
||||
// Add => {
|
||||
// text_state.push('+');
|
||||
// }
|
||||
// Apostrophe => {
|
||||
// text_state.push('\'');
|
||||
// }
|
||||
// At => {
|
||||
// text_state.push('@');
|
||||
// }
|
||||
// Backslash => {
|
||||
// text_state.push('\\');
|
||||
// }
|
||||
// Colon => {
|
||||
// text_state.push(':');
|
||||
// }
|
||||
// Period | Decimal => {
|
||||
// text_state.push('.');
|
||||
// }
|
||||
// Equals | NumpadEquals => {
|
||||
// text_state.push('=');
|
||||
// }
|
||||
// Grave => {
|
||||
// text_state.push('`');
|
||||
// }
|
||||
// Minus | Subtract => {
|
||||
// text_state.push('-');
|
||||
// }
|
||||
// Multiply => {
|
||||
// text_state.push('*');
|
||||
// }
|
||||
// Semicolon => {
|
||||
// text_state.push(';');
|
||||
// }
|
||||
// Slash | Divide => {
|
||||
// text_state.push('/');
|
||||
// }
|
||||
// Underline => {
|
||||
// text_state.push('_');
|
||||
// }
|
||||
// Yen => {
|
||||
// text_state.push('¥');
|
||||
// }
|
||||
// Copy => {
|
||||
// todo!("copy");
|
||||
// }
|
||||
// Paste => {
|
||||
// todo!("paste");
|
||||
// }
|
||||
// Cut => {
|
||||
// todo!("cut");
|
||||
// }
|
||||
// }
|
||||
// }
|
|
@ -11,19 +11,29 @@
|
|||
// re-enable this when working on performance optimizations than have it block PRs.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
use crate::rect::Rect;
|
||||
// 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::create_rect_buffers;
|
||||
use crate::text::{build_glyph_brush, Text};
|
||||
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::{Event, ModifiersState};
|
||||
use winit::event_loop::ControlFlow;
|
||||
|
||||
pub mod ast;
|
||||
pub mod bucket;
|
||||
mod buffer;
|
||||
pub mod file;
|
||||
mod keyboard_input;
|
||||
mod rect;
|
||||
pub mod text_state;
|
||||
pub mod text;
|
||||
mod util;
|
||||
mod vertex;
|
||||
|
||||
/// The editor is actually launched from the CLI if you pass it zero arguments,
|
||||
|
@ -51,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,
|
||||
|
@ -81,59 +91,21 @@ 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 rect_pipeline = make_rect_pipeline(&gpu_device, &swap_chain_descr);
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
// 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 = build_glyph_brush(&gpu_device, render_format)?;
|
||||
|
||||
let is_animating = true;
|
||||
let mut text_state = String::new();
|
||||
|
@ -152,178 +124,97 @@ 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);
|
||||
keyboard_input::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);
|
||||
}
|
||||
|
||||
glyph_brush.queue(Section {
|
||||
screen_position: (30.0, 30.0),
|
||||
bounds: (size.width as f32, size.height as f32),
|
||||
text: vec![Text::new("Enter some text:")
|
||||
.with_color([0.4666, 0.2, 1.0, 1.0])
|
||||
.with_scale(40.0)],
|
||||
..Section::default()
|
||||
});
|
||||
drop(render_pass);
|
||||
|
||||
glyph_brush.queue(Section {
|
||||
screen_position: (30.0, 90.0),
|
||||
bounds: (size.width as f32, size.height as f32),
|
||||
text: vec![Text::new(format!("{}|", text_state).as_str())
|
||||
.with_color([1.0, 1.0, 1.0, 1.0])
|
||||
.with_scale(40.0)],
|
||||
..Section::default()
|
||||
});
|
||||
|
||||
// Draw the text!
|
||||
glyph_brush
|
||||
.draw_queued(
|
||||
&device,
|
||||
draw_all_text(
|
||||
&gpu_device,
|
||||
&mut staging_belt,
|
||||
&mut encoder,
|
||||
&frame.view,
|
||||
size.width,
|
||||
size.height,
|
||||
)
|
||||
.expect("Draw queued");
|
||||
&frame,
|
||||
&size,
|
||||
&text_state,
|
||||
&mut glyph_brush,
|
||||
);
|
||||
|
||||
staging_belt.finish();
|
||||
queue.submit(Some(encoder.finish()));
|
||||
cmd_queue.submit(Some(encoder.finish()));
|
||||
|
||||
// Recall unused staging buffers
|
||||
use futures::task::SpawnExt;
|
||||
|
@ -341,27 +232,126 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
|
|||
})
|
||||
}
|
||||
|
||||
fn handle_keydown(
|
||||
elem_state: ElementState,
|
||||
virtual_keycode: VirtualKeyCode,
|
||||
_modifiers: ModifiersState,
|
||||
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,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn draw_all_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<()>,
|
||||
) {
|
||||
use winit::event::VirtualKeyCode::*;
|
||||
let bounds = (size.width as f32, size.height as f32).into();
|
||||
|
||||
if let ElementState::Released = elem_state {
|
||||
return;
|
||||
}
|
||||
let main_label = Text {
|
||||
position: (30.0, 30.0).into(),
|
||||
bounds,
|
||||
color: (0.4666, 0.2, 1.0, 1.0).into(),
|
||||
text: String::from("Enter some text:"),
|
||||
size: 40.0,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
match virtual_keycode {
|
||||
Copy => {
|
||||
todo!("copy");
|
||||
let code_text = Text {
|
||||
position: (30.0, 90.0).into(),
|
||||
bounds,
|
||||
color: (1.0, 1.0, 1.0, 1.0).into(),
|
||||
text: String::from(format!("{}|", text_state).as_str()),
|
||||
size: 40.0,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
text::queue_text_draw(&main_label, glyph_brush);
|
||||
|
||||
text::queue_text_draw(&code_text, glyph_brush);
|
||||
|
||||
glyph_brush
|
||||
.draw_queued(
|
||||
gpu_device,
|
||||
staging_belt,
|
||||
encoder,
|
||||
&frame.view,
|
||||
size.width,
|
||||
size.height,
|
||||
)
|
||||
.expect("Draw queued");
|
||||
}
|
||||
|
||||
fn update_text_state(text_state: &mut String, received_char: &char) {
|
||||
match received_char {
|
||||
'\u{8}' | '\u{7f}' => {
|
||||
// In Linux, we get a '\u{8}' when you press backspace,
|
||||
// but in macOS we get '\u{7f}'.
|
||||
text_state.pop();
|
||||
}
|
||||
Paste => {
|
||||
todo!("paste");
|
||||
'\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
|
||||
}
|
||||
Cut => {
|
||||
todo!("cut");
|
||||
_ => {
|
||||
text_state.push(*received_char);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
62
editor/src/text.rs
Normal file
62
editor/src/text.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Adapted from https://github.com/sotrh/learn-wgpu
|
||||
// by Benjamin Hansen, licensed under the MIT license
|
||||
|
||||
use ab_glyph::{FontArc, InvalidFont};
|
||||
use cgmath::{Vector2, Vector4};
|
||||
use wgpu_glyph::{ab_glyph, GlyphBrush, GlyphBrushBuilder, Section};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Text {
|
||||
pub position: Vector2<f32>,
|
||||
pub bounds: Vector2<f32>,
|
||||
pub color: Vector4<f32>,
|
||||
pub text: String,
|
||||
pub size: f32,
|
||||
pub visible: bool,
|
||||
pub centered: bool,
|
||||
}
|
||||
|
||||
impl Default for Text {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
position: (0.0, 0.0).into(),
|
||||
bounds: (std::f32::INFINITY, std::f32::INFINITY).into(),
|
||||
color: (1.0, 1.0, 1.0, 1.0).into(),
|
||||
text: String::new(),
|
||||
size: 16.0,
|
||||
visible: true,
|
||||
centered: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) {
|
||||
let layout = wgpu_glyph::Layout::default().h_align(if text.centered {
|
||||
wgpu_glyph::HorizontalAlign::Center
|
||||
} else {
|
||||
wgpu_glyph::HorizontalAlign::Left
|
||||
});
|
||||
|
||||
let section = Section {
|
||||
screen_position: text.position.into(),
|
||||
bounds: text.bounds.into(),
|
||||
layout,
|
||||
..Section::default()
|
||||
}
|
||||
.add_text(
|
||||
wgpu_glyph::Text::new(&text.text)
|
||||
.with_color(text.color)
|
||||
.with_scale(text.size),
|
||||
);
|
||||
|
||||
glyph_brush.queue(section);
|
||||
}
|
||||
|
||||
pub fn build_glyph_brush(
|
||||
gpu_device: &wgpu::Device,
|
||||
render_format: wgpu::TextureFormat,
|
||||
) -> Result<GlyphBrush<()>, InvalidFont> {
|
||||
let inconsolata = FontArc::try_from_slice(include_bytes!("../Inconsolata-Regular.ttf"))?;
|
||||
|
||||
Ok(GlyphBrushBuilder::using_font(inconsolata).build(&gpu_device, render_format))
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
use winit::event::{ElementState, ModifiersState, VirtualKeyCode};
|
||||
|
||||
pub fn handle_text_input(
|
||||
text_state: &mut String,
|
||||
elem_state: ElementState,
|
||||
virtual_keycode: VirtualKeyCode,
|
||||
_modifiers: ModifiersState,
|
||||
) {
|
||||
use winit::event::VirtualKeyCode::*;
|
||||
|
||||
if let ElementState::Released = elem_state {
|
||||
return;
|
||||
}
|
||||
|
||||
match virtual_keycode {
|
||||
Key1 | Numpad1 => text_state.push('1'),
|
||||
Key2 | Numpad2 => text_state.push('2'),
|
||||
Key3 | Numpad3 => text_state.push('3'),
|
||||
Key4 | Numpad4 => text_state.push('4'),
|
||||
Key5 | Numpad5 => text_state.push('5'),
|
||||
Key6 | Numpad6 => text_state.push('6'),
|
||||
Key7 | Numpad7 => text_state.push('7'),
|
||||
Key8 | Numpad8 => text_state.push('8'),
|
||||
Key9 | Numpad9 => text_state.push('9'),
|
||||
Key0 | Numpad0 => text_state.push('0'),
|
||||
A => text_state.push('a'),
|
||||
B => text_state.push('b'),
|
||||
C => text_state.push('c'),
|
||||
D => text_state.push('d'),
|
||||
E => text_state.push('e'),
|
||||
F => text_state.push('f'),
|
||||
G => text_state.push('g'),
|
||||
H => text_state.push('h'),
|
||||
I => text_state.push('i'),
|
||||
J => text_state.push('j'),
|
||||
K => text_state.push('k'),
|
||||
L => text_state.push('l'),
|
||||
M => text_state.push('m'),
|
||||
N => text_state.push('n'),
|
||||
O => text_state.push('o'),
|
||||
P => text_state.push('p'),
|
||||
Q => text_state.push('q'),
|
||||
R => text_state.push('r'),
|
||||
S => text_state.push('s'),
|
||||
T => text_state.push('t'),
|
||||
U => text_state.push('u'),
|
||||
V => text_state.push('v'),
|
||||
W => text_state.push('w'),
|
||||
X => text_state.push('x'),
|
||||
Y => text_state.push('y'),
|
||||
Z => text_state.push('z'),
|
||||
Escape | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | F14 | F15
|
||||
| F16 | F17 | F18 | F19 | F20 | F21 | F22 | F23 | F24 | Snapshot | Scroll | Pause
|
||||
| Insert | Home | Delete | End | PageDown | PageUp | Left | Up | Right | Down | Compose
|
||||
| Caret | Numlock | AbntC1 | AbntC2 | Ax | Calculator | Capital | Convert | Kana
|
||||
| Kanji | LAlt | LBracket | LControl | LShift | LWin | Mail | MediaSelect | PlayPause
|
||||
| Power | PrevTrack | MediaStop | Mute | MyComputer | NavigateForward
|
||||
| NavigateBackward | NextTrack | NoConvert | OEM102 | RAlt | Sysrq | RBracket
|
||||
| RControl | RShift | RWin | Sleep | Stop | Unlabeled | VolumeDown | VolumeUp | Wake
|
||||
| WebBack | WebFavorites | WebForward | WebHome | WebRefresh | WebSearch | Apps | Tab
|
||||
| WebStop => {
|
||||
// TODO handle
|
||||
}
|
||||
Back => {
|
||||
text_state.pop();
|
||||
}
|
||||
Return | NumpadEnter => {
|
||||
text_state.push('\n');
|
||||
}
|
||||
Space => {
|
||||
text_state.push(' ');
|
||||
}
|
||||
Comma | NumpadComma => {
|
||||
text_state.push(',');
|
||||
}
|
||||
Add => {
|
||||
text_state.push('+');
|
||||
}
|
||||
Apostrophe => {
|
||||
text_state.push('\'');
|
||||
}
|
||||
At => {
|
||||
text_state.push('@');
|
||||
}
|
||||
Backslash => {
|
||||
text_state.push('\\');
|
||||
}
|
||||
Colon => {
|
||||
text_state.push(':');
|
||||
}
|
||||
Period | Decimal => {
|
||||
text_state.push('.');
|
||||
}
|
||||
Equals | NumpadEquals => {
|
||||
text_state.push('=');
|
||||
}
|
||||
Grave => {
|
||||
text_state.push('`');
|
||||
}
|
||||
Minus | Subtract => {
|
||||
text_state.push('-');
|
||||
}
|
||||
Multiply => {
|
||||
text_state.push('*');
|
||||
}
|
||||
Semicolon => {
|
||||
text_state.push(';');
|
||||
}
|
||||
Slash | Divide => {
|
||||
text_state.push('/');
|
||||
}
|
||||
Underline => {
|
||||
text_state.push('_');
|
||||
}
|
||||
Yen => {
|
||||
text_state.push('¥');
|
||||
}
|
||||
Copy => {
|
||||
todo!("copy");
|
||||
}
|
||||
Paste => {
|
||||
todo!("paste");
|
||||
}
|
||||
Cut => {
|
||||
todo!("cut");
|
||||
}
|
||||
}
|
||||
}
|
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,25 +13,23 @@ 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 {
|
||||
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
|
||||
offset: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress,
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float3,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
26
editor/tests/modules/Simple.roc
Normal file
26
editor/tests/modules/Simple.roc
Normal file
|
@ -0,0 +1,26 @@
|
|||
interface Simple
|
||||
exposes [
|
||||
v, x
|
||||
]
|
||||
imports []
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
v : Str
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
v = "Value!"
|
||||
|
||||
|
||||
|
||||
x : Int
|
||||
x = 4
|
68
editor/tests/modules/Storage.roc
Normal file
68
editor/tests/modules/Storage.roc
Normal file
|
@ -0,0 +1,68 @@
|
|||
interface Storage
|
||||
exposes [
|
||||
Storage,
|
||||
decoder,
|
||||
get,
|
||||
listener,
|
||||
set
|
||||
]
|
||||
imports [
|
||||
Map.{ Map },
|
||||
Json.Decode.{ Decoder } as Decode
|
||||
Json.Encode as Encode
|
||||
Ports.FromJs as FromJs
|
||||
Ports.ToJs as ToJs
|
||||
]
|
||||
|
||||
|
||||
################################################################################
|
||||
## TYPES ##
|
||||
################################################################################
|
||||
|
||||
|
||||
Storage : [
|
||||
@Storage (Map Str Decode.Value)
|
||||
]
|
||||
|
||||
|
||||
################################################################################
|
||||
## API ##
|
||||
################################################################################
|
||||
|
||||
|
||||
get : Storage, Str, Decoder a -> [ Ok a, NotInStorage, DecodeError Decode.Error ]*
|
||||
get = \key, decoder, @Storage map ->
|
||||
when Map.get map key is
|
||||
Ok json ->
|
||||
Decode.decodeValue decoder json
|
||||
|
||||
Err NotFound ->
|
||||
NotInStorage
|
||||
|
||||
|
||||
set : Encode.Value, Str -> Effect {}
|
||||
set json str =
|
||||
ToJs.type "setStorage"
|
||||
|> ToJs.setFields [
|
||||
Field "key" (Encode.str str),
|
||||
Field "value" json
|
||||
]
|
||||
|> ToJs.send
|
||||
|
||||
|
||||
decoder : Decoder Storage
|
||||
decoder =
|
||||
Decode.mapType Decode.value
|
||||
|> Decode.map \map -> @Storage map
|
||||
|
||||
|
||||
################################################################################
|
||||
## PORTS INCOMING ##
|
||||
################################################################################
|
||||
|
||||
|
||||
listener : (Storage -> msg) -> FromJs.Listener msg
|
||||
listener toMsg =
|
||||
FromJs.listen "storageUpdated"
|
||||
(Decode.map decoder toMsg)
|
||||
|
40
editor/tests/test_file.rs
Normal file
40
editor/tests/test_file.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
#[macro_use]
|
||||
extern crate indoc;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_file {
|
||||
use bumpalo::Bump;
|
||||
use roc_editor::file::File;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn read_and_fmt_simple_roc_module() {
|
||||
let simple_module_path = Path::new("./tests/modules/Simple.roc");
|
||||
|
||||
let arena = Bump::new();
|
||||
|
||||
let file = File::read(simple_module_path, &arena)
|
||||
.expect("Could not read Simple.roc in test_file test");
|
||||
|
||||
assert_eq!(
|
||||
file.fmt(),
|
||||
indoc!(
|
||||
r#"
|
||||
interface Simple
|
||||
exposes [
|
||||
v, x
|
||||
]
|
||||
imports []
|
||||
|
||||
v : Str
|
||||
|
||||
v = "Value!"
|
||||
|
||||
x : Int
|
||||
x = 4"#
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
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,6 +1,6 @@
|
|||
app "closure" provides [ makeClosure ] to "./platform/"
|
||||
|
||||
makeClosure : ({} -> Int) as MyClosure
|
||||
makeClosure : ({} -> I64) as MyClosure
|
||||
makeClosure =
|
||||
x = 42
|
||||
y = 42
|
||||
|
|
|
@ -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
|
|
@ -5,7 +5,7 @@ ConsList a : [ Cons a (ConsList a), Nil ]
|
|||
empty : ConsList a
|
||||
empty = Nil
|
||||
|
||||
len : ConsList a -> Int
|
||||
len : ConsList a -> I64
|
||||
len = \list ->
|
||||
when list is
|
||||
Cons _ rest -> 1 + len rest
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
app "effect-example" imports [ Effect ] provides [ main ] to "./platform"
|
||||
app "effect-example"
|
||||
packages { base: "thing/platform-dir" }
|
||||
imports [ base.Task.{ Task, after } ]
|
||||
provides [ main ] to base
|
||||
|
||||
|
||||
main : Effect.Effect {} as Fx
|
||||
# TODO `main : Task {}` does not work
|
||||
# it will then think that the `Task` module is unused
|
||||
# (if we also don't use any of the other importd symbols)
|
||||
main : Task.Task {} as Fx
|
||||
main =
|
||||
when if 1 == 1 then True 3 else False 3.14 is
|
||||
True n -> Effect.putLine (Str.fromInt n)
|
||||
_ -> Effect.putLine "Yay"
|
||||
True n -> Task.putLine (Str.fromInt n)
|
||||
_ -> Task.putLine "Yay"
|
||||
|
||||
# main : Effect.Effect {} as Fx
|
||||
# main =
|
||||
|
|
|
@ -21,11 +21,11 @@ singleton = \key, value ->
|
|||
Node Black key value Empty Empty
|
||||
|
||||
# {-| Determine the number of key-value pairs in the dictionary. -}
|
||||
size : Dict k v -> Int
|
||||
size : Dict k v -> I64
|
||||
size = \dict ->
|
||||
sizeHelp 0 dict
|
||||
|
||||
sizeHelp : Int, Dict k v -> Int
|
||||
sizeHelp : I64, Dict k v -> I64
|
||||
sizeHelp = \n, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
|
|
|
@ -8,6 +8,6 @@ edition = "2018"
|
|||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
roc_std = { path = "../../../roc_std" }
|
||||
roc_std = { path = "../../../../roc_std" }
|
||||
|
||||
[workspace]
|
|
@ -1,6 +1,6 @@
|
|||
platform folkertdev/foo
|
||||
requires { main : Effect {} }
|
||||
exposes []
|
||||
exposes [ Task ]
|
||||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
9
examples/effect/thing/platform-dir/Task.roc
Normal file
9
examples/effect/thing/platform-dir/Task.roc
Normal file
|
@ -0,0 +1,9 @@
|
|||
interface Task
|
||||
exposes [ Task, putLine, after ]
|
||||
imports [ Effect ]
|
||||
|
||||
Task a : Effect.Effect a
|
||||
|
||||
putLine = \line -> Effect.putLine line
|
||||
|
||||
after = Effect.after
|
|
@ -1,9 +1,9 @@
|
|||
app "quicksort" imports [ Utils.{ swap } ] provides [ quicksort ] to "./platform"
|
||||
|
||||
|
||||
quicksort : List Int -> List Int
|
||||
quicksort : List I64 -> List I64
|
||||
quicksort = \originalList ->
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
|
@ -14,7 +14,7 @@ quicksort = \originalList ->
|
|||
else
|
||||
list
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -26,7 +26,7 @@ quicksort = \originalList ->
|
|||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
interface Utils exposes [ swap ] imports []
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
|
|
@ -2,7 +2,7 @@ app "quicksort" provides [ quicksort ] to "./platform"
|
|||
|
||||
quicksort = \originalList ->
|
||||
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
|
@ -14,7 +14,7 @@ quicksort = \originalList ->
|
|||
list
|
||||
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -25,7 +25,7 @@ quicksort = \originalList ->
|
|||
Err _ ->
|
||||
Pair (low - 1) initialList
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
@ -41,7 +41,7 @@ quicksort = \originalList ->
|
|||
Pair i list
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
|
1
examples/shared-quicksort/.gitignore
vendored
Normal file
1
examples/shared-quicksort/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
quicksort
|
|
@ -1,12 +1,12 @@
|
|||
app "quicksort" packages { base: "./platform" } provides [ quicksort ] to base
|
||||
|
||||
quicksort : List Int -> List Int
|
||||
quicksort : List I64 -> List I64
|
||||
quicksort = \originalList -> helper originalList
|
||||
|
||||
helper : List Int -> List Int
|
||||
helper : List I64 -> List I64
|
||||
helper = \originalList ->
|
||||
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp : List (Num a), I64, I64 -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
|
@ -18,7 +18,7 @@ helper = \originalList ->
|
|||
list
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap : I64, I64, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
|
@ -29,7 +29,7 @@ helper = \originalList ->
|
|||
_ ->
|
||||
[]
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition : I64, I64, List (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
|
@ -41,7 +41,7 @@ helper = \originalList ->
|
|||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp : I64, I64, List (Num a), I64, (Num a) -> [ Pair I64 (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
|
|
|
@ -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