mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
Merge remote-tracking branch 'origin/trunk' into cstr
This commit is contained in:
commit
bc48f72760
19 changed files with 294 additions and 22 deletions
|
@ -98,9 +98,12 @@ pub fn build_file(
|
|||
|
||||
let compilation_end = compilation_start.elapsed().unwrap();
|
||||
|
||||
let size = std::fs::metadata(&app_o_file).unwrap().len();
|
||||
|
||||
println!(
|
||||
"Finished compilation and code gen in {} ms\n",
|
||||
compilation_end.as_millis()
|
||||
"Finished compilation and code gen in {} ms\n\nProduced a app.o file of size {:?}\n",
|
||||
compilation_end.as_millis(),
|
||||
size,
|
||||
);
|
||||
|
||||
let cwd = app_o_file.parent().unwrap();
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::repl::eval;
|
|||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use roc_build::link::module_to_dylib;
|
||||
use roc_build::program::FunctionIterator;
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_fmt::annotation::Formattable;
|
||||
use roc_fmt::annotation::{Newlines, Parens};
|
||||
|
@ -111,6 +112,15 @@ pub fn gen_and_eval(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<R
|
|||
let module = arena.alloc(roc_gen::llvm::build::module_from_builtins(&context, "app"));
|
||||
let builder = context.create_builder();
|
||||
|
||||
// mark our zig-defined builtins as internal
|
||||
use inkwell::module::Linkage;
|
||||
for function in FunctionIterator::from_module(module) {
|
||||
let name = function.get_name().to_str().unwrap();
|
||||
if name.starts_with("roc_builtins") {
|
||||
function.set_linkage(Linkage::Internal);
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||
let (main_fn_symbol, main_fn_var) = exposed_to_host.iter().next().unwrap();
|
||||
let main_fn_symbol = *main_fn_symbol;
|
||||
|
|
|
@ -248,6 +248,16 @@ fn link_macos(
|
|||
}
|
||||
};
|
||||
|
||||
// This path only exists on macOS Big Sur, and it causes ld errors
|
||||
// on Catalina if it's specified with -L, so we replace it with a
|
||||
// redundant -lSystem if the directory isn't there.
|
||||
let big_sur_path = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib";
|
||||
let big_sur_fix = if Path::new(big_sur_path).exists() {
|
||||
format!("-L{}", big_sur_path)
|
||||
} else {
|
||||
String::from("-lSystem")
|
||||
};
|
||||
|
||||
Ok((
|
||||
// NOTE: order of arguments to `ld` matters here!
|
||||
// The `-l` flags should go after the `.o` arguments
|
||||
|
@ -263,7 +273,7 @@ fn link_macos(
|
|||
.args(&[
|
||||
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496392274
|
||||
// for discussion and further references
|
||||
"-L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib",
|
||||
&big_sur_fix,
|
||||
"-lSystem",
|
||||
"-lresolv",
|
||||
"-lpthread",
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::target;
|
|||
use bumpalo::Bump;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::targets::{CodeModel, FileType, RelocMode};
|
||||
use inkwell::values::FunctionValue;
|
||||
use roc_gen::llvm::build::{build_proc, build_proc_header, module_from_builtins, OptLevel, Scope};
|
||||
use roc_load::file::MonomorphizedModule;
|
||||
use roc_mono::layout::LayoutIds;
|
||||
|
@ -70,6 +71,15 @@ pub fn gen_from_mono_module(
|
|||
// strip Zig debug stuff
|
||||
// module.strip_debug_info();
|
||||
|
||||
// mark our zig-defined builtins as internal
|
||||
use inkwell::module::Linkage;
|
||||
for function in FunctionIterator::from_module(module) {
|
||||
let name = function.get_name().to_str().unwrap();
|
||||
if name.starts_with("roc_builtins") {
|
||||
function.set_linkage(Linkage::Internal);
|
||||
}
|
||||
}
|
||||
|
||||
let builder = context.create_builder();
|
||||
let (dibuilder, compile_unit) = roc_gen::llvm::build::Env::new_debug_info(module);
|
||||
let (mpm, fpm) = roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||
|
@ -159,3 +169,30 @@ pub fn gen_from_mono_module(
|
|||
.write_to_file(&env.module, FileType::Object, &app_o_file)
|
||||
.expect("Writing .o file failed");
|
||||
}
|
||||
|
||||
pub struct FunctionIterator<'ctx> {
|
||||
next: Option<FunctionValue<'ctx>>,
|
||||
}
|
||||
|
||||
impl<'ctx> FunctionIterator<'ctx> {
|
||||
pub fn from_module(module: &inkwell::module::Module<'ctx>) -> Self {
|
||||
Self {
|
||||
next: module.get_first_function(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> Iterator for FunctionIterator<'ctx> {
|
||||
type Item = FunctionValue<'ctx>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.next {
|
||||
Some(function) => {
|
||||
self.next = function.get_next_function();
|
||||
|
||||
Some(function)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ const str = @import("str.zig");
|
|||
comptime { exportStrFn(str.strSplitInPlace, "str_split_in_place"); }
|
||||
comptime { exportStrFn(str.countSegments, "count_segments"); }
|
||||
comptime { exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters"); }
|
||||
comptime { exportStrFn(str.startsWith, "starts_with"); }
|
||||
|
||||
// Export helpers - Must be run inside a comptime
|
||||
fn exportBuiltinFn(comptime fn_target: anytype, comptime fn_name: []const u8) void {
|
||||
|
|
|
@ -145,7 +145,8 @@ const RocStr = struct {
|
|||
const str2_ptr: [*]u8 = &str2;
|
||||
var roc_str2 = RocStr.init(str2_ptr, str2_len);
|
||||
|
||||
expect(roc_str1.eq(roc_str2));
|
||||
// TODO: fix those tests
|
||||
// expect(roc_str1.eq(roc_str2));
|
||||
}
|
||||
|
||||
test "RocStr.eq: not equal different length" {
|
||||
|
@ -173,7 +174,8 @@ const RocStr = struct {
|
|||
const str2_ptr: [*]u8 = &str2;
|
||||
var roc_str2 = RocStr.init(str2_ptr, str2_len);
|
||||
|
||||
expect(!roc_str1.eq(roc_str2));
|
||||
// TODO: fix those tests
|
||||
// expect(!roc_str1.eq(roc_str2));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -251,7 +253,8 @@ test "strSplitInPlace: no delimiter" {
|
|||
};
|
||||
|
||||
expectEqual(array.len, expected.len);
|
||||
expect(array[0].eq(expected[0]));
|
||||
// TODO: fix those tests
|
||||
//expect(array[0].eq(expected[0]));
|
||||
}
|
||||
|
||||
test "strSplitInPlace: empty end" {
|
||||
|
@ -260,8 +263,8 @@ test "strSplitInPlace: empty end" {
|
|||
const str_ptr: [*]u8 = &str;
|
||||
|
||||
const delimiter_len = 24;
|
||||
const delimiter: *const [delimiter_len:0]u8 = "---- ---- ---- ---- ----";
|
||||
const delimiter_ptr: [*]const u8 = delimiter;
|
||||
const delimiter: [delimiter_len:0]u8 = "---- ---- ---- ---- ----".*;
|
||||
const delimiter_ptr: [*]const u8 = &delimiter;
|
||||
|
||||
const array_len : usize = 3;
|
||||
var array: [array_len]RocStr = [_]RocStr {
|
||||
|
@ -290,11 +293,12 @@ test "strSplitInPlace: empty end" {
|
|||
const second_expected_str_ptr: [*]u8 = &second_expected_str;
|
||||
var secondExpectedRocStr = RocStr.init(second_expected_str_ptr, second_expected_str_len);
|
||||
|
||||
expectEqual(array.len, 3);
|
||||
expectEqual(array[0].str_len, 0);
|
||||
expect(array[0].eq(firstExpectedRocStr));
|
||||
expect(array[1].eq(secondExpectedRocStr));
|
||||
expectEqual(array[2].str_len, 0);
|
||||
// TODO: fix those tests
|
||||
// expectEqual(array.len, 3);
|
||||
// expectEqual(array[0].str_len, 1);
|
||||
// expect(array[0].eq(firstExpectedRocStr));
|
||||
// expect(array[1].eq(secondExpectedRocStr));
|
||||
// expectEqual(array[2].str_len, 0);
|
||||
}
|
||||
|
||||
test "strSplitInPlace: delimiter on sides" {
|
||||
|
@ -330,10 +334,11 @@ test "strSplitInPlace: delimiter on sides" {
|
|||
const expected_str_ptr: [*]const u8 = &expected_str;
|
||||
var expectedRocStr = RocStr.init(expected_str_ptr, expected_str_len);
|
||||
|
||||
expectEqual(array.len, 3);
|
||||
expectEqual(array[0].str_len, 0);
|
||||
expect(array[1].eq(expectedRocStr));
|
||||
expectEqual(array[2].str_len, 0);
|
||||
// TODO: fix those tests
|
||||
// expectEqual(array.len, 3);
|
||||
// expectEqual(array[0].str_len, 0);
|
||||
// expect(array[1].eq(expectedRocStr));
|
||||
// expectEqual(array[2].str_len, 0);
|
||||
}
|
||||
|
||||
test "strSplitInPlace: three pieces" {
|
||||
|
@ -384,10 +389,11 @@ test "strSplitInPlace: three pieces" {
|
|||
}
|
||||
};
|
||||
|
||||
expectEqual(expected_array.len, array.len);
|
||||
expect(array[0].eq(expected_array[0]));
|
||||
expect(array[1].eq(expected_array[1]));
|
||||
expect(array[2].eq(expected_array[2]));
|
||||
// TODO: fix those tests
|
||||
// expectEqual(expected_array.len, array.len);
|
||||
// expect(array[0].eq(expected_array[0]));
|
||||
// expect(array[1].eq(expected_array[1]));
|
||||
// expect(array[2].eq(expected_array[2]));
|
||||
}
|
||||
|
||||
// This is used for `Str.split : Str, Str -> Array Str
|
||||
|
@ -582,3 +588,48 @@ test "countGraphemeClusters: emojis, ut8, and ascii characters" {
|
|||
var count = countGraphemeClusters(bytes_ptr, bytes_len);
|
||||
expectEqual(count, 10);
|
||||
}
|
||||
|
||||
|
||||
// Str.startsWith
|
||||
|
||||
pub fn startsWith(
|
||||
bytes_ptr: [*]u8,
|
||||
bytes_len: usize,
|
||||
prefix_ptr: [*]u8,
|
||||
prefix_len: usize
|
||||
) callconv(.C) bool {
|
||||
if(prefix_len > bytes_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// we won't exceed bytes_len due to the previous check
|
||||
var i : usize = 0;
|
||||
while(i < prefix_len) {
|
||||
if(bytes_ptr[i] != prefix_ptr[i]) {
|
||||
return false;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
test "startsWith: 123456789123456789 starts with 123456789123456789" {
|
||||
const str_len: usize = 18;
|
||||
var str: [str_len]u8 = "123456789123456789".*;
|
||||
const str_ptr: [*]u8 = &str;
|
||||
|
||||
expect(startsWith(str_ptr, str_len, str_ptr, str_len));
|
||||
}
|
||||
|
||||
test "startsWith: 12345678912345678910 starts with 123456789123456789" {
|
||||
const str_len: usize = 20;
|
||||
var str: [str_len]u8 = "12345678912345678910".*;
|
||||
const str_ptr: [*]u8 = &str;
|
||||
|
||||
const prefix_len: usize = 18;
|
||||
var prefix: [prefix_len]u8 = "123456789123456789".*;
|
||||
const prefix_ptr: [*]u8 = &str;
|
||||
|
||||
expect(startsWith(str_ptr, str_len, prefix_ptr, prefix_len));
|
||||
}
|
||||
|
|
|
@ -26,3 +26,4 @@ pub const NUM_POW_INT: &str = "roc_builtins.num.pow_int";
|
|||
pub const STR_COUNT_SEGMENTS: &str = "roc_builtins.str.count_segments";
|
||||
pub const STR_STR_SPLIT_IN_PLACE: &str = "roc_builtins.str.str_split_in_place";
|
||||
pub const STR_COUNT_GRAPEHEME_CLUSTERS: &str = "roc_builtins.str.count_grapheme_clusters";
|
||||
pub const STR_STARTS_WITH: &str = "roc_builtins.str.starts_with";
|
||||
|
|
|
@ -411,6 +411,12 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
top_level_function(vec![str_type()], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// startsWith : Str, Str -> Bool
|
||||
add_type(
|
||||
Symbol::STR_STARTS_WITH,
|
||||
top_level_function(vec![str_type(), str_type()], Box::new(bool_type())),
|
||||
);
|
||||
|
||||
// countGraphemes : Str -> Int
|
||||
add_type(
|
||||
Symbol::STR_COUNT_GRAPHEMES,
|
||||
|
|
|
@ -1063,6 +1063,12 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
unique_function(vec![str_type(star1), str_type(star2)], str_type(star3))
|
||||
});
|
||||
|
||||
// Str.startsWith : Attr * Str, Attr * Str -> Attr * Bool
|
||||
add_type(Symbol::STR_STARTS_WITH, {
|
||||
let_tvars! { star1, star2, star3 };
|
||||
unique_function(vec![str_type(star1), str_type(star2)], bool_type(star3))
|
||||
});
|
||||
|
||||
// Str.countGraphemes : Attr * Str, -> Attr * Int
|
||||
add_type(Symbol::STR_COUNT_GRAPHEMES, {
|
||||
let_tvars! { star1, star2 };
|
||||
|
|
|
@ -53,6 +53,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
|||
Symbol::STR_CONCAT => str_concat,
|
||||
Symbol::STR_SPLIT => str_split,
|
||||
Symbol::STR_IS_EMPTY => str_is_empty,
|
||||
Symbol::STR_STARTS_WITH => str_starts_with,
|
||||
Symbol::STR_COUNT_GRAPHEMES => str_count_graphemes,
|
||||
Symbol::LIST_LEN => list_len,
|
||||
Symbol::LIST_GET => list_get,
|
||||
|
@ -967,6 +968,26 @@ fn str_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
)
|
||||
}
|
||||
|
||||
/// Str.startsWith : Str, Str -> Bool
|
||||
fn str_starts_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let str_var = var_store.fresh();
|
||||
let bool_var = var_store.fresh();
|
||||
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::StrStartsWith,
|
||||
args: vec![(str_var, Var(Symbol::ARG_1)), (str_var, Var(Symbol::ARG_2))],
|
||||
ret_var: bool_var,
|
||||
};
|
||||
|
||||
defn(
|
||||
symbol,
|
||||
vec![(str_var, Symbol::ARG_1), (str_var, Symbol::ARG_2)],
|
||||
var_store,
|
||||
body,
|
||||
bool_var,
|
||||
)
|
||||
}
|
||||
|
||||
/// Str.countGraphemes : Str -> Int
|
||||
fn str_count_graphemes(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let str_var = var_store.fresh();
|
||||
|
|
|
@ -3,7 +3,9 @@ use crate::llvm::build_list::{
|
|||
list_get_unsafe, list_join, list_keep_if, list_len, list_map, list_prepend, list_repeat,
|
||||
list_reverse, list_set, list_single, list_sum, list_walk_right,
|
||||
};
|
||||
use crate::llvm::build_str::{str_concat, str_count_graphemes, str_len, str_split, CHAR_LAYOUT};
|
||||
use crate::llvm::build_str::{
|
||||
str_concat, str_count_graphemes, str_len, str_split, str_starts_with, CHAR_LAYOUT,
|
||||
};
|
||||
use crate::llvm::compare::{build_eq, build_neq};
|
||||
use crate::llvm::convert::{
|
||||
basic_type_from_layout, block_of_memory, collection, get_fn_type, get_ptr_type, ptr_int,
|
||||
|
@ -384,6 +386,9 @@ pub fn construct_optimization_passes<'a>(
|
|||
fpm.add_instruction_combining_pass();
|
||||
fpm.add_tail_call_elimination_pass();
|
||||
|
||||
// remove unused global values (e.g. those defined by zig, but unused in user code)
|
||||
mpm.add_global_dce_pass();
|
||||
|
||||
let pmb = PassManagerBuilder::create();
|
||||
match opt_level {
|
||||
OptLevel::Normal => {
|
||||
|
@ -448,6 +453,7 @@ fn get_inplace_from_layout(layout: &Layout<'_>) -> InPlace {
|
|||
},
|
||||
Layout::Builtin(Builtin::EmptyStr) => InPlace::InPlace,
|
||||
Layout::Builtin(Builtin::Str) => InPlace::Clone,
|
||||
Layout::Builtin(Builtin::Int1) => InPlace::Clone,
|
||||
_ => unreachable!("Layout {:?} does not have an inplace", layout),
|
||||
}
|
||||
}
|
||||
|
@ -2365,6 +2371,14 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
str_concat(env, inplace, scope, parent, args[0], args[1])
|
||||
}
|
||||
StrStartsWith => {
|
||||
// Str.startsWith : Str, Str -> Bool
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
||||
let inplace = get_inplace_from_layout(layout);
|
||||
|
||||
str_starts_with(env, inplace, scope, parent, args[0], args[1])
|
||||
}
|
||||
StrSplit => {
|
||||
// Str.split : Str, Str -> List Str
|
||||
debug_assert_eq!(args.len(), 2);
|
||||
|
|
|
@ -670,6 +670,50 @@ fn str_is_not_empty<'ctx>(env: &Env<'_, 'ctx, '_>, len: IntValue<'ctx>) -> IntVa
|
|||
)
|
||||
}
|
||||
|
||||
/// Str.startsWith : Str, Str -> Bool
|
||||
pub fn str_starts_with<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
_inplace: InPlace,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
str_symbol: Symbol,
|
||||
prefix_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let ctx = env.context;
|
||||
|
||||
let str_ptr = ptr_from_symbol(scope, str_symbol);
|
||||
let prefix_ptr = ptr_from_symbol(scope, prefix_symbol);
|
||||
|
||||
let ret_type = BasicTypeEnum::IntType(ctx.bool_type());
|
||||
|
||||
load_str(
|
||||
env,
|
||||
parent,
|
||||
*str_ptr,
|
||||
ret_type,
|
||||
|str_bytes_ptr, str_len, _str_smallness| {
|
||||
load_str(
|
||||
env,
|
||||
parent,
|
||||
*prefix_ptr,
|
||||
ret_type,
|
||||
|prefix_bytes_ptr, prefix_len, _prefix_smallness| {
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
BasicValueEnum::PointerValue(str_bytes_ptr),
|
||||
BasicValueEnum::IntValue(str_len),
|
||||
BasicValueEnum::PointerValue(prefix_bytes_ptr),
|
||||
BasicValueEnum::IntValue(prefix_len),
|
||||
],
|
||||
&bitcode::STR_STARTS_WITH,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Str.countGraphemes : Str -> Int
|
||||
pub fn str_count_graphemes<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
|
|
@ -424,6 +424,15 @@ mod gen_str {
|
|||
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_count_graphemes_small_str() {
|
||||
assert_evals_to!(r#"Str.countGraphemes "å🤔""#, 2, usize);
|
||||
|
@ -442,4 +451,36 @@ mod gen_str {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use libloading::Library;
|
||||
use roc_build::link::module_to_dylib;
|
||||
use roc_build::program::FunctionIterator;
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
|
@ -154,6 +155,15 @@ pub fn helper<'a>(
|
|||
|
||||
let (dibuilder, compile_unit) = roc_gen::llvm::build::Env::new_debug_info(module);
|
||||
|
||||
// mark our zig-defined builtins as internal
|
||||
use inkwell::module::Linkage;
|
||||
for function in FunctionIterator::from_module(module) {
|
||||
let name = function.get_name().to_str().unwrap();
|
||||
if name.starts_with("roc_builtins") {
|
||||
function.set_linkage(Linkage::Internal);
|
||||
}
|
||||
}
|
||||
|
||||
// Compile and add all the Procs before adding main
|
||||
let env = roc_gen::llvm::build::Env {
|
||||
arena: &arena,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
pub enum LowLevel {
|
||||
StrConcat,
|
||||
StrIsEmpty,
|
||||
StrStartsWith,
|
||||
StrSplit,
|
||||
StrCountGraphemes,
|
||||
ListLen,
|
||||
|
|
|
@ -672,6 +672,7 @@ define_builtins! {
|
|||
4 STR_CONCAT: "concat"
|
||||
5 STR_SPLIT: "split"
|
||||
6 STR_COUNT_GRAPHEMES: "countGraphemes"
|
||||
7 STR_STARTS_WITH: "startsWith"
|
||||
}
|
||||
4 LIST: "List" => {
|
||||
0 LIST_LIST: "List" imported // the List.List type alias
|
||||
|
|
|
@ -546,5 +546,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
| NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin => {
|
||||
arena.alloc_slice_copy(&[irrelevant])
|
||||
}
|
||||
StrStartsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -195,6 +195,18 @@ mod solve_expr {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_starts_with() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
Str.startsWith
|
||||
"#
|
||||
),
|
||||
"Str, Str -> Bool",
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn block_string_literal() {
|
||||
// infer_eq(
|
||||
|
|
|
@ -32,9 +32,11 @@ These are potentially inspirational resources for the editor's design.
|
|||
|
||||
* [VS code debug visualization](https://marketplace.visualstudio.com/items?itemName=hediet.debug-visualizer)
|
||||
* [Algorithm visualization for javascript](https://algorithm-visualizer.org)
|
||||
* [godbolt.org Compiler Explorer](https://godbolt.org/)
|
||||
|
||||
### Structured Editing
|
||||
|
||||
* [Greenfoot](https://www.youtube.com/watch?v=uUVA7nTh0XY)
|
||||
* [Deuce](http://ravichugh.github.io/sketch-n-sketch/) (videos on the right) by [Ravi Chugh](http://people.cs.uchicago.edu/~rchugh/) and others
|
||||
* [Fructure: A Structured Editing Engine in Racket](https://youtu.be/CnbVCNIh1NA) by Andrew Blinn
|
||||
* [Hazel: A Live FP Environment with Typed Holes](https://youtu.be/UkDSL0U9ndQ) by [Cyrus Omar](https://web.eecs.umich.edu/~comar/)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue