mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
Merge branch 'trunk' into test-linker
This commit is contained in:
commit
d9e28ed4b1
11 changed files with 1152 additions and 35 deletions
|
@ -3,6 +3,7 @@ use roc_build::{
|
|||
link::{link, rebuild_host, LinkType},
|
||||
program,
|
||||
};
|
||||
use roc_builtins::bitcode;
|
||||
use roc_can::builtins::builtin_defs_map;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_load::file::LoadingProblem;
|
||||
|
@ -240,11 +241,19 @@ pub fn build_file<'a>(
|
|||
})?;
|
||||
BuildOutcome::NoProblems
|
||||
} else {
|
||||
let mut inputs = vec![
|
||||
host_input_path.as_path().to_str().unwrap(),
|
||||
app_o_file.to_str().unwrap(),
|
||||
];
|
||||
if matches!(opt_level, OptLevel::Development) {
|
||||
inputs.push(bitcode::OBJ_PATH);
|
||||
}
|
||||
|
||||
let (mut child, _) = // TODO use lld
|
||||
link(
|
||||
target,
|
||||
binary_path.clone(),
|
||||
&[host_input_path.as_path().to_str().unwrap(), app_o_file.to_str().unwrap()],
|
||||
&inputs,
|
||||
link_type
|
||||
)
|
||||
.map_err(|_| {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::target::arch_str;
|
||||
#[cfg(feature = "llvm")]
|
||||
use libloading::{Error, Library};
|
||||
use roc_builtins::bitcode;
|
||||
#[cfg(feature = "llvm")]
|
||||
use roc_mono::ir::OptLevel;
|
||||
use std::collections::HashMap;
|
||||
|
@ -93,7 +94,12 @@ pub fn build_zig_host_native(
|
|||
.env("PATH", env_path)
|
||||
.env("HOME", env_home);
|
||||
if let Some(shared_lib_path) = shared_lib_path {
|
||||
command.args(&["build-exe", "-fPIE", shared_lib_path.to_str().unwrap()]);
|
||||
command.args(&[
|
||||
"build-exe",
|
||||
"-fPIE",
|
||||
shared_lib_path.to_str().unwrap(),
|
||||
bitcode::OBJ_PATH,
|
||||
]);
|
||||
} else {
|
||||
command.args(&["build-obj", "-fPIC"]);
|
||||
}
|
||||
|
@ -109,7 +115,6 @@ pub fn build_zig_host_native(
|
|||
// include libc
|
||||
"--library",
|
||||
"c",
|
||||
"--strip",
|
||||
// cross-compile?
|
||||
"-target",
|
||||
target,
|
||||
|
@ -178,7 +183,12 @@ pub fn build_zig_host_native(
|
|||
.env("PATH", &env_path)
|
||||
.env("HOME", &env_home);
|
||||
if let Some(shared_lib_path) = shared_lib_path {
|
||||
command.args(&["build-exe", "-fPIE", shared_lib_path.to_str().unwrap()]);
|
||||
command.args(&[
|
||||
"build-exe",
|
||||
"-fPIE",
|
||||
shared_lib_path.to_str().unwrap(),
|
||||
bitcode::OBJ_PATH,
|
||||
]);
|
||||
} else {
|
||||
command.args(&["build-obj", "-fPIC"]);
|
||||
}
|
||||
|
@ -197,7 +207,6 @@ pub fn build_zig_host_native(
|
|||
// include libc
|
||||
"--library",
|
||||
"c",
|
||||
"--strip",
|
||||
]);
|
||||
if matches!(opt_level, OptLevel::Optimize) {
|
||||
command.args(&["-O", "ReleaseSafe"]);
|
||||
|
@ -274,6 +283,7 @@ pub fn build_c_host_native(
|
|||
if let Some(shared_lib_path) = shared_lib_path {
|
||||
command.args(&[
|
||||
shared_lib_path.to_str().unwrap(),
|
||||
bitcode::OBJ_PATH,
|
||||
"-fPIE",
|
||||
"-pie",
|
||||
"-lm",
|
||||
|
|
|
@ -164,7 +164,12 @@ test "" {
|
|||
// https://github.com/ziglang/zig/blob/85755c51d529e7d9b406c6bdf69ce0a0f33f3353/lib/std/special/compiler_rt/muloti4.zig
|
||||
//
|
||||
// Thank you Zig Contributors!
|
||||
export fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {
|
||||
|
||||
// Export it as weak incase it is alreadly linked in by something else.
|
||||
comptime {
|
||||
@export(__muloti4, .{ .name = "__muloti4", .linkage = .Weak });
|
||||
}
|
||||
fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {
|
||||
// @setRuntimeSafety(std.builtin.is_test);
|
||||
|
||||
const min = @bitCast(i128, @as(u128, 1 << (128 - 1)));
|
||||
|
|
|
@ -226,6 +226,7 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
|||
|
||||
#[inline(always)]
|
||||
fn load_args<'a>(
|
||||
_buf: &mut Vec<'a, u8>,
|
||||
_symbol_map: &mut MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
|
||||
_args: &'a [(Layout<'a>, Symbol)],
|
||||
_ret_layout: &Layout<'a>,
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::marker::PhantomData;
|
|||
pub mod aarch64;
|
||||
pub mod x86_64;
|
||||
|
||||
const PTR_SIZE: u32 = 64;
|
||||
const PTR_SIZE: u32 = 8;
|
||||
|
||||
pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||
const GENERAL_PARAM_REGS: &'static [GeneralReg];
|
||||
|
@ -48,6 +48,7 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
|||
|
||||
// load_args updates the symbol map to know where every arg is stored.
|
||||
fn load_args<'a>(
|
||||
buf: &mut Vec<'a, u8>,
|
||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
||||
|
@ -422,7 +423,12 @@ impl<
|
|||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> Result<(), String> {
|
||||
CC::load_args(&mut self.symbol_storage_map, args, ret_layout)?;
|
||||
CC::load_args(
|
||||
&mut self.buf,
|
||||
&mut self.symbol_storage_map,
|
||||
args,
|
||||
ret_layout,
|
||||
)?;
|
||||
// Update used and free regs.
|
||||
for (sym, storage) in &self.symbol_storage_map {
|
||||
match storage {
|
||||
|
@ -489,6 +495,25 @@ impl<
|
|||
ASM::mov_freg64_freg64(&mut self.buf, dst_reg, CC::FLOAT_RETURN_REGS[0]);
|
||||
Ok(())
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
if CC::returns_via_arg_pointer(ret_layout)? {
|
||||
// This will happen on windows, return via pointer here.
|
||||
Err("FnCall: Returning strings via pointer not yet implemented".to_string())
|
||||
} else {
|
||||
let offset = self.claim_stack_size(16)?;
|
||||
self.symbol_storage_map.insert(
|
||||
*dst,
|
||||
SymbolStorage::Base {
|
||||
offset,
|
||||
size: 16,
|
||||
owned: true,
|
||||
},
|
||||
);
|
||||
ASM::mov_base32_reg64(&mut self.buf, offset, CC::GENERAL_RETURN_REGS[0]);
|
||||
ASM::mov_base32_reg64(&mut self.buf, offset + 8, CC::GENERAL_RETURN_REGS[1]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
x => Err(format!(
|
||||
"FnCall: receiving return type, {:?}, is not yet implemented",
|
||||
x
|
||||
|
@ -893,6 +918,35 @@ impl<
|
|||
ASM::mov_freg64_imm64(&mut self.buf, &mut self.relocs, reg, val);
|
||||
Ok(())
|
||||
}
|
||||
Literal::Str(x) if x.len() < 16 => {
|
||||
// Load small string.
|
||||
let reg = self.get_tmp_general_reg()?;
|
||||
|
||||
let offset = self.claim_stack_size(16)?;
|
||||
self.symbol_storage_map.insert(
|
||||
*sym,
|
||||
SymbolStorage::Base {
|
||||
offset,
|
||||
size: 16,
|
||||
owned: true,
|
||||
},
|
||||
);
|
||||
let mut bytes = [0; 16];
|
||||
bytes[..x.len()].copy_from_slice(x.as_bytes());
|
||||
bytes[15] = (x.len() as u8) | 0b1000_0000;
|
||||
|
||||
let mut num_bytes = [0; 8];
|
||||
num_bytes.copy_from_slice(&bytes[..8]);
|
||||
let num = i64::from_ne_bytes(num_bytes);
|
||||
ASM::mov_reg64_imm64(&mut self.buf, reg, num);
|
||||
ASM::mov_base32_reg64(&mut self.buf, offset, reg);
|
||||
|
||||
num_bytes.copy_from_slice(&bytes[8..]);
|
||||
let num = i64::from_ne_bytes(num_bytes);
|
||||
ASM::mov_reg64_imm64(&mut self.buf, reg, num);
|
||||
ASM::mov_base32_reg64(&mut self.buf, offset + 8, reg);
|
||||
Ok(())
|
||||
}
|
||||
x => Err(format!("loading literal, {:?}, is not yet implemented", x)),
|
||||
}
|
||||
}
|
||||
|
@ -1012,6 +1066,19 @@ impl<
|
|||
Layout::Builtin(Builtin::Float64) => {
|
||||
ASM::mov_freg64_base32(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *offset);
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
if self.symbol_storage_map.contains_key(&Symbol::RET_POINTER) {
|
||||
// This will happen on windows, return via pointer here.
|
||||
return Err("Returning strings via pointer not yet implemented".to_string());
|
||||
} else {
|
||||
ASM::mov_reg64_base32(&mut self.buf, CC::GENERAL_RETURN_REGS[0], *offset);
|
||||
ASM::mov_reg64_base32(
|
||||
&mut self.buf,
|
||||
CC::GENERAL_RETURN_REGS[1],
|
||||
*offset + 8,
|
||||
);
|
||||
}
|
||||
}
|
||||
Layout::Struct(field_layouts) => {
|
||||
let (offset, size) = (*offset, *size);
|
||||
// Nothing to do for empty struct
|
||||
|
@ -1446,8 +1513,6 @@ macro_rules! single_register_integers {
|
|||
#[macro_export]
|
||||
macro_rules! single_register_floats {
|
||||
() => {
|
||||
// Float16 is explicitly ignored because it is not supported by must hardware and may require special exceptions.
|
||||
// Builtin::Float16 |
|
||||
Builtin::Float32 | Builtin::Float64
|
||||
};
|
||||
}
|
||||
|
|
|
@ -177,6 +177,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
|
||||
#[inline(always)]
|
||||
fn load_args<'a>(
|
||||
buf: &mut Vec<'a, u8>,
|
||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
|
@ -231,6 +232,29 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
);
|
||||
}
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
if general_i + 1 < Self::GENERAL_PARAM_REGS.len() {
|
||||
// Load the value to the param reg.
|
||||
let dst1 = Self::GENERAL_PARAM_REGS[general_i];
|
||||
let dst2 = Self::GENERAL_PARAM_REGS[general_i + 1];
|
||||
base_offset += 16;
|
||||
X86_64Assembler::mov_reg64_base32(buf, dst1, base_offset - 8);
|
||||
X86_64Assembler::mov_reg64_base32(buf, dst2, base_offset);
|
||||
symbol_map.insert(
|
||||
*sym,
|
||||
SymbolStorage::Base {
|
||||
offset: base_offset,
|
||||
size: 16,
|
||||
owned: true,
|
||||
},
|
||||
);
|
||||
general_i += 2;
|
||||
} else {
|
||||
return Err(
|
||||
"loading strings args on the stack is not yet implemented".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
Layout::Struct(&[]) => {}
|
||||
x => {
|
||||
return Err(format!(
|
||||
|
@ -257,7 +281,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
// For most return layouts we will do nothing.
|
||||
// In some cases, we need to put the return address as the first arg.
|
||||
match ret_layout {
|
||||
Layout::Builtin(single_register_builtins!()) => {}
|
||||
Layout::Builtin(single_register_builtins!() | Builtin::Str) => {}
|
||||
x => {
|
||||
return Err(format!(
|
||||
"receiving return type, {:?}, is not yet implemented",
|
||||
|
@ -373,6 +397,32 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
stack_offset += 8;
|
||||
}
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
if general_i + 1 < Self::GENERAL_PARAM_REGS.len() {
|
||||
// Load the value to the param reg.
|
||||
let dst1 = Self::GENERAL_PARAM_REGS[general_i];
|
||||
let dst2 = Self::GENERAL_PARAM_REGS[general_i + 1];
|
||||
match symbol_map
|
||||
.get(&args[i])
|
||||
.ok_or("function argument does not reference any symbol")?
|
||||
{
|
||||
SymbolStorage::Base { offset, .. } => {
|
||||
X86_64Assembler::mov_reg64_base32(buf, dst1, *offset);
|
||||
X86_64Assembler::mov_reg64_base32(buf, dst2, *offset + 8);
|
||||
}
|
||||
_ => {
|
||||
return Err("Strings only support being loaded from base offsets"
|
||||
.to_string());
|
||||
}
|
||||
}
|
||||
general_i += 2;
|
||||
} else {
|
||||
return Err(
|
||||
"calling functions with strings on the stack is not yet implemented"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Layout::Struct(&[]) => {}
|
||||
x => {
|
||||
return Err(format!(
|
||||
|
@ -516,6 +566,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
|
||||
#[inline(always)]
|
||||
fn load_args<'a>(
|
||||
_buf: &mut Vec<'a, u8>,
|
||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
|
@ -535,9 +586,18 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
Layout::Builtin(single_register_integers!()) => {
|
||||
symbol_map
|
||||
.insert(*sym, SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[i]));
|
||||
i += 1;
|
||||
}
|
||||
Layout::Builtin(single_register_floats!()) => {
|
||||
symbol_map.insert(*sym, SymbolStorage::FloatReg(Self::FLOAT_PARAM_REGS[i]));
|
||||
i += 1;
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
// I think this just needs to be passed on the stack, so not a huge deal.
|
||||
return Err(
|
||||
"Passing str args with Windows fast call not yet implemented."
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Layout::Struct(&[]) => {}
|
||||
x => {
|
||||
|
@ -547,7 +607,6 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
));
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
} else {
|
||||
base_offset += match layout {
|
||||
Layout::Builtin(single_register_builtins!()) => 8,
|
||||
|
@ -580,7 +639,6 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
ret_layout: &Layout<'a>,
|
||||
) -> Result<u32, String> {
|
||||
let mut stack_offset = Self::SHADOW_SPACE_SIZE as i32;
|
||||
let mut reg_i = 0;
|
||||
// For most return layouts we will do nothing.
|
||||
// In some cases, we need to put the return address as the first arg.
|
||||
match ret_layout {
|
||||
|
@ -597,7 +655,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
Layout::Builtin(single_register_integers!()) => {
|
||||
if i < Self::GENERAL_PARAM_REGS.len() {
|
||||
// Load the value to the param reg.
|
||||
let dst = Self::GENERAL_PARAM_REGS[reg_i];
|
||||
let dst = Self::GENERAL_PARAM_REGS[i];
|
||||
match symbol_map
|
||||
.get(&args[i])
|
||||
.ok_or("function argument does not reference any symbol")?
|
||||
|
@ -615,7 +673,6 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
)
|
||||
}
|
||||
}
|
||||
reg_i += 1;
|
||||
} else {
|
||||
// Load the value to the stack.
|
||||
match symbol_map
|
||||
|
@ -651,7 +708,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
Layout::Builtin(single_register_floats!()) => {
|
||||
if i < Self::FLOAT_PARAM_REGS.len() {
|
||||
// Load the value to the param reg.
|
||||
let dst = Self::FLOAT_PARAM_REGS[reg_i];
|
||||
let dst = Self::FLOAT_PARAM_REGS[i];
|
||||
match symbol_map
|
||||
.get(&args[i])
|
||||
.ok_or("function argument does not reference any symbol")?
|
||||
|
@ -668,7 +725,6 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
return Err("Cannot load general symbol into FloatReg".to_string())
|
||||
}
|
||||
}
|
||||
reg_i += 1;
|
||||
} else {
|
||||
// Load the value to the stack.
|
||||
match symbol_map
|
||||
|
@ -700,6 +756,12 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
stack_offset += 8;
|
||||
}
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
// I think this just needs to be passed on the stack, so not a huge deal.
|
||||
return Err(
|
||||
"Passing str args with Windows fast call not yet implemented.".to_string(),
|
||||
);
|
||||
}
|
||||
Layout::Struct(&[]) => {}
|
||||
x => {
|
||||
return Err(format!(
|
||||
|
|
|
@ -93,12 +93,8 @@ where
|
|||
for (layout, sym) in proc.args {
|
||||
self.set_layout_map(*sym, layout)?;
|
||||
}
|
||||
// let start = std::time::Instant::now();
|
||||
self.scan_ast(&proc.body);
|
||||
self.create_free_map();
|
||||
// let duration = start.elapsed();
|
||||
// println!("Time to calculate lifetimes: {:?}", duration);
|
||||
// println!("{:?}", self.last_seen_map());
|
||||
self.build_stmt(&proc.body, &proc.ret_layout)?;
|
||||
self.finalize()
|
||||
}
|
||||
|
@ -119,6 +115,11 @@ where
|
|||
self.free_symbols(stmt)?;
|
||||
Ok(())
|
||||
}
|
||||
Stmt::Refcounting(_modify, following) => {
|
||||
// TODO: actually deal with refcounting. For hello world, we just skipped it.
|
||||
self.build_stmt(following, ret_layout)?;
|
||||
Ok(())
|
||||
}
|
||||
Stmt::Switch {
|
||||
cond_symbol,
|
||||
cond_layout,
|
||||
|
@ -298,6 +299,13 @@ where
|
|||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
Symbol::STR_CONCAT => self.build_run_low_level(
|
||||
sym,
|
||||
&LowLevel::StrConcat,
|
||||
arguments,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
x if x
|
||||
.module_string(&self.env().interns)
|
||||
.starts_with(ModuleName::APP) =>
|
||||
|
@ -470,6 +478,13 @@ where
|
|||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
LowLevel::StrConcat => self.build_fn_call(
|
||||
sym,
|
||||
bitcode::STR_CONCAT.to_string(),
|
||||
args,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
x => Err(format!("low level, {:?}. is not yet implemented", x)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
|
||||
#[macro_use]
|
||||
extern crate indoc;
|
||||
|
||||
extern crate bumpalo;
|
||||
extern crate libc;
|
||||
|
||||
#[macro_use]
|
||||
mod helpers;
|
||||
|
||||
|
|
954
compiler/gen_dev/tests/dev_str.rs
Normal file
954
compiler/gen_dev/tests/dev_str.rs
Normal file
|
@ -0,0 +1,954 @@
|
|||
// #[macro_use]
|
||||
// extern crate indoc;
|
||||
|
||||
#[macro_use]
|
||||
mod helpers;
|
||||
|
||||
#[cfg(all(test, any(target_os = "linux", target_os = "macos"), any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
|
||||
mod dev_str {
|
||||
// use roc_std::{RocList, RocStr};
|
||||
// #[test]
|
||||
// fn str_split_bigger_delimiter_small_str() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// List.len (Str.split "hello" "JJJJ there")
|
||||
// "#
|
||||
// ),
|
||||
// 1,
|
||||
// i64
|
||||
// );
|
||||
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when List.first (Str.split "JJJ" "JJJJ there") is
|
||||
// Ok str ->
|
||||
// Str.countGraphemes str
|
||||
|
||||
// _ ->
|
||||
// -1
|
||||
|
||||
// "#
|
||||
// ),
|
||||
// 3,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_split_str_concat_repeated() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when List.first (Str.split "JJJJJ" "JJJJ there") is
|
||||
// Ok str ->
|
||||
// str
|
||||
// |> Str.concat str
|
||||
// |> Str.concat str
|
||||
// |> Str.concat str
|
||||
// |> Str.concat str
|
||||
|
||||
// _ ->
|
||||
// "Not Str!"
|
||||
|
||||
// "#
|
||||
// ),
|
||||
// RocStr::from_slice(b"JJJJJJJJJJJJJJJJJJJJJJJJJ"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_split_small_str_bigger_delimiter() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when
|
||||
// List.first
|
||||
// (Str.split "JJJ" "0123456789abcdefghi")
|
||||
// is
|
||||
// Ok str -> str
|
||||
// _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// RocStr::from_slice(b"JJJ"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_split_big_str_small_delimiter() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// Str.split "01234567789abcdefghi?01234567789abcdefghi" "?"
|
||||
// "#
|
||||
// ),
|
||||
// RocList::from_slice(&[
|
||||
// RocStr::from_slice(b"01234567789abcdefghi"),
|
||||
// RocStr::from_slice(b"01234567789abcdefghi")
|
||||
// ]),
|
||||
// RocList<RocStr>
|
||||
// );
|
||||
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// Str.split "01234567789abcdefghi 3ch 01234567789abcdefghi" "3ch"
|
||||
// "#
|
||||
// ),
|
||||
// RocList::from_slice(&[
|
||||
// RocStr::from_slice(b"01234567789abcdefghi "),
|
||||
// RocStr::from_slice(b" 01234567789abcdefghi")
|
||||
// ]),
|
||||
// RocList<RocStr>
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_split_small_str_small_delimiter() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// Str.split "J!J!J" "!"
|
||||
// "#
|
||||
// ),
|
||||
// RocList::from_slice(&[
|
||||
// RocStr::from_slice(b"J"),
|
||||
// RocStr::from_slice(b"J"),
|
||||
// RocStr::from_slice(b"J")
|
||||
// ]),
|
||||
// RocList<RocStr>
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_split_bigger_delimiter_big_strs() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// Str.split
|
||||
// "string to split is shorter"
|
||||
// "than the delimiter which happens to be very very long"
|
||||
// "#
|
||||
// ),
|
||||
// RocList::from_slice(&[RocStr::from_slice(b"string to split is shorter")]),
|
||||
// RocList<RocStr>
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_split_empty_strs() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// Str.split "" ""
|
||||
// "#
|
||||
// ),
|
||||
// RocList::from_slice(&[RocStr::from_slice(b"")]),
|
||||
// RocList<RocStr>
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_split_minimal_example() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// Str.split "a," ","
|
||||
// "#
|
||||
// ),
|
||||
// RocList::from_slice(&[RocStr::from_slice(b"a"), RocStr::from_slice(b"")]),
|
||||
// RocList<RocStr>
|
||||
// )
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_split_small_str_big_delimiter() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// Str.split
|
||||
// "1---- ---- ---- ---- ----2---- ---- ---- ---- ----"
|
||||
// "---- ---- ---- ---- ----"
|
||||
// |> List.len
|
||||
// "#
|
||||
// ),
|
||||
// 3,
|
||||
// i64
|
||||
// );
|
||||
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// Str.split
|
||||
// "1---- ---- ---- ---- ----2---- ---- ---- ---- ----"
|
||||
// "---- ---- ---- ---- ----"
|
||||
// "#
|
||||
// ),
|
||||
// RocList::from_slice(&[
|
||||
// RocStr::from_slice(b"1"),
|
||||
// RocStr::from_slice(b"2"),
|
||||
// RocStr::from_slice(b"")
|
||||
// ]),
|
||||
// RocList<RocStr>
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_split_small_str_20_char_delimiter() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// Str.split
|
||||
// "3|-- -- -- -- -- -- |4|-- -- -- -- -- -- |"
|
||||
// "|-- -- -- -- -- -- |"
|
||||
// "#
|
||||
// ),
|
||||
// RocList::from_slice(&[
|
||||
// RocStr::from_slice(b"3"),
|
||||
// RocStr::from_slice(b"4"),
|
||||
// RocStr::from_slice(b"")
|
||||
// ]),
|
||||
// RocList<RocStr>
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_concat_big_to_big() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// Str.concat
|
||||
// "First string that is fairly long. Longer strings make for different errors. "
|
||||
// "Second string that is also fairly long. Two long strings test things that might not appear with short strings."
|
||||
// "#
|
||||
// ),
|
||||
// RocStr::from_slice(b"First string that is fairly long. Longer strings make for different errors. Second string that is also fairly long. Two long strings test things that might not appear with short strings."),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn small_str_literal() {
|
||||
assert_evals_to!(
|
||||
"\"JJJJJJJJJJJJJJJ\"",
|
||||
[
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0b1000_1111
|
||||
],
|
||||
[u8; 16]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn small_str_zeroed_literal() {
|
||||
// // Verifies that we zero out unused bytes in the string.
|
||||
// // This is important so that string equality tests don't randomly
|
||||
// // fail due to unused memory being there!
|
||||
// assert_evals_to!(
|
||||
// "\"J\"",
|
||||
// [
|
||||
// 0x4a,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0x00,
|
||||
// 0b1000_0001
|
||||
// ],
|
||||
// [u8; 16]
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn small_str_concat_empty_first_arg() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "" "JJJJJJJJJJJJJJJ""#,
|
||||
[
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0b1000_1111
|
||||
],
|
||||
[u8; 16]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn small_str_concat_empty_second_arg() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "JJJJJJJJJJJJJJJ" """#,
|
||||
[
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0b1000_1111
|
||||
],
|
||||
[u8; 16]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn small_str_concat_small_to_big() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.concat "abc" " this is longer than 15 chars""#,
|
||||
// RocStr::from_slice(b"abc this is longer than 15 chars"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn small_str_concat_small_to_small_staying_small() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "J" "JJJJJJJJJJJJJJ""#,
|
||||
[
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0b1000_1111
|
||||
],
|
||||
[u8; 16]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn small_str_concat_small_to_small_overflow_to_big() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.concat "abcdefghijklm" "nopqrstuvwxyz""#,
|
||||
// RocStr::from_slice(b"abcdefghijklmnopqrstuvwxyz"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_concat_empty() {
|
||||
// assert_evals_to!(r#"Str.concat "" """#, RocStr::default(), RocStr);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn small_str_is_empty() {
|
||||
// assert_evals_to!(r#"Str.isEmpty "abc""#, false, bool);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn big_str_is_empty() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.isEmpty "this is more than 15 chars long""#,
|
||||
// false,
|
||||
// bool
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn empty_str_is_empty() {
|
||||
// assert_evals_to!(r#"Str.isEmpty """#, true, bool);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with() {
|
||||
// assert_evals_to!(r#"Str.startsWith "hello world" "hell""#, true, bool);
|
||||
// assert_evals_to!(r#"Str.startsWith "hello world" """#, true, bool);
|
||||
// assert_evals_to!(r#"Str.startsWith "nope" "hello world""#, false, bool);
|
||||
// assert_evals_to!(r#"Str.startsWith "hell" "hello world""#, false, bool);
|
||||
// assert_evals_to!(r#"Str.startsWith "" "hello world""#, false, bool);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with_code_point() {
|
||||
// assert_evals_to!(
|
||||
// &format!(r#"Str.startsWithCodePt "foobar" {}"#, 'f' as u32),
|
||||
// true,
|
||||
// bool
|
||||
// );
|
||||
// assert_evals_to!(
|
||||
// &format!(r#"Str.startsWithCodePt "zoobar" {}"#, 'f' as u32),
|
||||
// false,
|
||||
// bool
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_ends_with() {
|
||||
// assert_evals_to!(r#"Str.endsWith "hello world" "world""#, true, bool);
|
||||
// assert_evals_to!(r#"Str.endsWith "nope" "hello world""#, false, bool);
|
||||
// assert_evals_to!(r#"Str.endsWith "" "hello world""#, false, bool);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_count_graphemes_small_str() {
|
||||
// assert_evals_to!(r#"Str.countGraphemes "å🤔""#, 2, usize);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_count_graphemes_three_js() {
|
||||
// assert_evals_to!(r#"Str.countGraphemes "JJJ""#, 3, usize);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_count_graphemes_big_str() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.countGraphemes "6🤔å🤔e¥🤔çppkd🙃1jdal🦯asdfa∆ltråø˚waia8918.,🏅jjc""#,
|
||||
// 45,
|
||||
// usize
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with_same_big_str() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.startsWith "123456789123456789" "123456789123456789""#,
|
||||
// true,
|
||||
// bool
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with_different_big_str() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.startsWith "12345678912345678910" "123456789123456789""#,
|
||||
// true,
|
||||
// bool
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with_same_small_str() {
|
||||
// assert_evals_to!(r#"Str.startsWith "1234" "1234""#, true, bool);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_starts_with_different_small_str() {
|
||||
// assert_evals_to!(r#"Str.startsWith "1234" "12""#, true, bool);
|
||||
// }
|
||||
// #[test]
|
||||
// fn str_starts_with_false_small_str() {
|
||||
// assert_evals_to!(r#"Str.startsWith "1234" "23""#, false, bool);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_int() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.fromInt 1234"#,
|
||||
// roc_std::RocStr::from_slice("1234".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// assert_evals_to!(
|
||||
// r#"Str.fromInt 0"#,
|
||||
// roc_std::RocStr::from_slice("0".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// assert_evals_to!(
|
||||
// r#"Str.fromInt -1"#,
|
||||
// roc_std::RocStr::from_slice("-1".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
|
||||
// let max = format!("{}", i64::MAX);
|
||||
// assert_evals_to!(
|
||||
// r#"Str.fromInt Num.maxInt"#,
|
||||
// RocStr::from_slice(max.as_bytes()),
|
||||
// RocStr
|
||||
// );
|
||||
|
||||
// let min = format!("{}", i64::MIN);
|
||||
// assert_evals_to!(
|
||||
// r#"Str.fromInt Num.minInt"#,
|
||||
// RocStr::from_slice(min.as_bytes()),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_pass_single_ascii() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 97 ] is
|
||||
// Ok val -> val
|
||||
// Err _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("a".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_pass_many_ascii() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 97, 98, 99, 0x7E ] is
|
||||
// Ok val -> val
|
||||
// Err _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("abc~".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_pass_single_unicode() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 0xE2, 0x88, 0x86 ] is
|
||||
// Ok val -> val
|
||||
// Err _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("∆".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_pass_many_unicode() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 0xE2, 0x88, 0x86, 0xC5, 0x93, 0xC2, 0xAC ] is
|
||||
// Ok val -> val
|
||||
// Err _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("∆œ¬".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_pass_single_grapheme() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 0xF0, 0x9F, 0x92, 0x96 ] is
|
||||
// Ok val -> val
|
||||
// Err _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("💖".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_pass_many_grapheme() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 0xF0, 0x9F, 0x92, 0x96, 0xF0, 0x9F, 0xA4, 0xA0, 0xF0, 0x9F, 0x9A, 0x80 ] is
|
||||
// Ok val -> val
|
||||
// Err _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("💖🤠🚀".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_pass_all() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 0xF0, 0x9F, 0x92, 0x96, 98, 0xE2, 0x88, 0x86 ] is
|
||||
// Ok val -> val
|
||||
// Err _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("💖b∆".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_fail_invalid_start_byte() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 97, 98, 0x80, 99 ] is
|
||||
// Err (BadUtf8 InvalidStartByte byteIndex) ->
|
||||
// if byteIndex == 2 then
|
||||
// "a"
|
||||
// else
|
||||
// "b"
|
||||
// _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("a".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_fail_unexpected_end_of_sequence() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 97, 98, 99, 0xC2 ] is
|
||||
// Err (BadUtf8 UnexpectedEndOfSequence byteIndex) ->
|
||||
// if byteIndex == 3 then
|
||||
// "a"
|
||||
// else
|
||||
// "b"
|
||||
// _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("a".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_fail_expected_continuation() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 97, 98, 99, 0xC2, 0x00 ] is
|
||||
// Err (BadUtf8 ExpectedContinuation byteIndex) ->
|
||||
// if byteIndex == 3 then
|
||||
// "a"
|
||||
// else
|
||||
// "b"
|
||||
// _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("a".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_fail_overlong_encoding() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 97, 0xF0, 0x80, 0x80, 0x80 ] is
|
||||
// Err (BadUtf8 OverlongEncoding byteIndex) ->
|
||||
// if byteIndex == 1 then
|
||||
// "a"
|
||||
// else
|
||||
// "b"
|
||||
// _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("a".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_fail_codepoint_too_large() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 97, 0xF4, 0x90, 0x80, 0x80 ] is
|
||||
// Err (BadUtf8 CodepointTooLarge byteIndex) ->
|
||||
// if byteIndex == 1 then
|
||||
// "a"
|
||||
// else
|
||||
// "b"
|
||||
// _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("a".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_fail_surrogate_half() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when Str.fromUtf8 [ 97, 98, 0xED, 0xA0, 0x80 ] is
|
||||
// Err (BadUtf8 EncodesSurrogateHalf byteIndex) ->
|
||||
// if byteIndex == 2 then
|
||||
// "a"
|
||||
// else
|
||||
// "b"
|
||||
// _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// roc_std::RocStr::from_slice("a".as_bytes()),
|
||||
// roc_std::RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_equality() {
|
||||
// assert_evals_to!(r#""a" == "a""#, true, bool);
|
||||
// assert_evals_to!(
|
||||
// r#""loremipsumdolarsitamet" == "loremipsumdolarsitamet""#,
|
||||
// true,
|
||||
// bool
|
||||
// );
|
||||
// assert_evals_to!(r#""a" != "b""#, true, bool);
|
||||
// assert_evals_to!(r#""a" == "b""#, false, bool);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_clone() {
|
||||
// use roc_std::RocStr;
|
||||
// let long = RocStr::from_slice("loremipsumdolarsitamet".as_bytes());
|
||||
// let short = RocStr::from_slice("x".as_bytes());
|
||||
// let empty = RocStr::from_slice("".as_bytes());
|
||||
|
||||
// debug_assert_eq!(long.clone(), long);
|
||||
// debug_assert_eq!(short.clone(), short);
|
||||
// debug_assert_eq!(empty.clone(), empty);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn nested_recursive_literal() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// Expr : [ Add Expr Expr, Val I64, Var I64 ]
|
||||
|
||||
// expr : Expr
|
||||
// expr = Add (Add (Val 3) (Val 1)) (Add (Val 1) (Var 1))
|
||||
|
||||
// printExpr : Expr -> Str
|
||||
// printExpr = \e ->
|
||||
// when e is
|
||||
// Add a b ->
|
||||
// "Add ("
|
||||
// |> Str.concat (printExpr a)
|
||||
// |> Str.concat ") ("
|
||||
// |> Str.concat (printExpr b)
|
||||
// |> Str.concat ")"
|
||||
// Val v -> "Val " |> Str.concat (Str.fromInt v)
|
||||
// Var v -> "Var " |> Str.concat (Str.fromInt v)
|
||||
|
||||
// printExpr expr
|
||||
// "#
|
||||
// ),
|
||||
// RocStr::from_slice(b"Add (Add (Val 3) (Val 1)) (Add (Val 1) (Var 1))"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_join_comma_small() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.joinWith ["1", "2"] ", " "#,
|
||||
// RocStr::from("1, 2"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_join_comma_big() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.joinWith ["10000000", "2000000", "30000000"] ", " "#,
|
||||
// RocStr::from("10000000, 2000000, 30000000"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_join_comma_single() {
|
||||
// assert_evals_to!(r#"Str.joinWith ["1"] ", " "#, RocStr::from("1"), RocStr);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_float() {
|
||||
// assert_evals_to!(r#"Str.fromFloat 3.14"#, RocStr::from("3.14"), RocStr);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_to_utf8() {
|
||||
// assert_evals_to!(
|
||||
// r#"Str.toUtf8 "hello""#,
|
||||
// RocList::from_slice(&[104, 101, 108, 108, 111]),
|
||||
// RocList<u8>
|
||||
// );
|
||||
// assert_evals_to!(
|
||||
// r#"Str.toUtf8 "this is a long string""#,
|
||||
// RocList::from_slice(&[
|
||||
// 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 108, 111, 110, 103, 32, 115, 116,
|
||||
// 114, 105, 110, 103
|
||||
// ]),
|
||||
// RocList<u8>
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_range() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// bytes = Str.toUtf8 "hello"
|
||||
// when Str.fromUtf8Range bytes { count: 5, start: 0 } is
|
||||
// Ok utf8String -> utf8String
|
||||
// _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// RocStr::from("hello"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_range_slice() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// bytes = Str.toUtf8 "hello"
|
||||
// when Str.fromUtf8Range bytes { count: 4, start: 1 } is
|
||||
// Ok utf8String -> utf8String
|
||||
// _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// RocStr::from("ello"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_range_slice_not_end() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// bytes = Str.toUtf8 "hello"
|
||||
// when Str.fromUtf8Range bytes { count: 3, start: 1 } is
|
||||
// Ok utf8String -> utf8String
|
||||
// _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// RocStr::from("ell"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_range_order_does_not_matter() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// bytes = Str.toUtf8 "hello"
|
||||
// when Str.fromUtf8Range bytes { start: 1, count: 3 } is
|
||||
// Ok utf8String -> utf8String
|
||||
// _ -> ""
|
||||
// "#
|
||||
// ),
|
||||
// RocStr::from("ell"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_range_out_of_bounds_start_value() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// bytes = Str.toUtf8 "hello"
|
||||
// when Str.fromUtf8Range bytes { start: 7, count: 3 } is
|
||||
// Ok _ -> ""
|
||||
// Err (BadUtf8 _ _) -> ""
|
||||
// Err OutOfBounds -> "out of bounds"
|
||||
// "#
|
||||
// ),
|
||||
// RocStr::from("out of bounds"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_range_count_too_high() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// bytes = Str.toUtf8 "hello"
|
||||
// when Str.fromUtf8Range bytes { start: 0, count: 6 } is
|
||||
// Ok _ -> ""
|
||||
// Err (BadUtf8 _ _) -> ""
|
||||
// Err OutOfBounds -> "out of bounds"
|
||||
// "#
|
||||
// ),
|
||||
// RocStr::from("out of bounds"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn str_from_utf8_range_count_too_high_for_start() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// bytes = Str.toUtf8 "hello"
|
||||
// when Str.fromUtf8Range bytes { start: 4, count: 3 } is
|
||||
// Ok _ -> ""
|
||||
// Err (BadUtf8 _ _) -> ""
|
||||
// Err OutOfBounds -> "out of bounds"
|
||||
// "#
|
||||
// ),
|
||||
// RocStr::from("out of bounds"),
|
||||
// RocStr
|
||||
// );
|
||||
// }
|
||||
}
|
|
@ -11,6 +11,12 @@ async function roc_web_platform_run(wasm_filename, callback) {
|
|||
|
||||
const importObj = {
|
||||
wasi_snapshot_preview1: {
|
||||
proc_exit: (code) => {
|
||||
if (code !== 0) {
|
||||
console.error(`Exited with code ${code}`);
|
||||
}
|
||||
exit_code = code;
|
||||
},
|
||||
roc_panic: (_pointer, _tag_id) => {
|
||||
throw 'Roc panicked!';
|
||||
}
|
||||
|
|
|
@ -48,25 +48,21 @@ export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
|||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
extern fn roc__mainForHost_1_exposed(*RocCallResult) void;
|
||||
|
||||
const RocCallResult = extern struct { flag: u64, content: RocStr };
|
||||
extern fn roc__mainForHost_1_exposed(*RocStr) void;
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
||||
extern fn js_display_roc_string(str_bytes: ?[*]u8, str_len: usize) void;
|
||||
|
||||
pub fn main() u8 {
|
||||
// make space for the result
|
||||
var callresult = RocCallResult{ .flag = 0, .content = RocStr.empty() };
|
||||
|
||||
// actually call roc to populate the callresult
|
||||
var callresult = RocStr.empty();
|
||||
roc__mainForHost_1_exposed(&callresult);
|
||||
|
||||
// display the result using JavaScript
|
||||
js_display_roc_string(callresult.content.str_bytes, callresult.content.str_len);
|
||||
js_display_roc_string(callresult.asU8ptr(), callresult.len());
|
||||
|
||||
callresult.content.deinit();
|
||||
callresult.deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue