Skip to content

Commit db97062

Browse files
committed
Merge remote-tracking branch 'origin/main' into propagate_is_eq
2 parents 2b4a344 + 45b9d81 commit db97062

File tree

9 files changed

+287
-0
lines changed

9 files changed

+287
-0
lines changed

src/build/builtin_compiler/main.zig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,18 @@ fn replaceStrIsEmptyWithLowLevel(env: *ModuleEnv) !std.ArrayList(CIR.Def.Idx) {
104104
if (env.common.findIdent("Builtin.Str.concat")) |str_concat_ident| {
105105
try low_level_map.put(str_concat_ident, .str_concat);
106106
}
107+
if (env.common.findIdent("Builtin.Str.contains")) |str_contains_ident| {
108+
try low_level_map.put(str_contains_ident, .str_contains);
109+
}
107110
if (env.common.findIdent("Builtin.Str.trim")) |str_trim_ident| {
108111
try low_level_map.put(str_trim_ident, .str_trim);
109112
}
113+
if (env.common.findIdent("Builtin.Str.trim_start")) |str_trim_start_ident| {
114+
try low_level_map.put(str_trim_start_ident, .str_trim_start);
115+
}
116+
if (env.common.findIdent("Builtin.Str.trim_end")) |str_trim_end_ident| {
117+
try low_level_map.put(str_trim_end_ident, .str_trim_end);
118+
}
110119
if (env.common.findIdent("Builtin.Str.caseless_ascii_equals")) |str_caseless_ascii_equals_ident| {
111120
try low_level_map.put(str_caseless_ascii_equals_ident, .str_caseless_ascii_equals);
112121
}

src/build/roc/Builtin.roc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ Builtin :: [].{
22
Str :: [ProvidedByCompiler].{
33
is_empty : Str -> Bool
44
concat : Str, Str -> Str
5+
contains : Str, Str -> Bool
56
trim : Str -> Str
7+
trim_start : Str -> Str
8+
trim_end : Str -> Str
69
caseless_ascii_equals : Str, Str -> Bool
710
with_ascii_lowercased : Str -> Str
811
with_ascii_uppercased : Str -> Str

src/builtins/str.zig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,11 @@ pub fn strConcat(
801801
}
802802
}
803803

804+
/// Str.contains
805+
pub fn strContains(haystack: RocStr, needle: RocStr) callconv(.c) bool {
806+
return std.mem.indexOf(u8, haystack.asSlice(), needle.asSlice()) != null;
807+
}
808+
804809
/// TODO: Document RocListStr.
805810
pub const RocListStr = extern struct {
806811
list_elements: ?[*]RocStr,

src/canonicalize/Expression.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,10 @@ pub const Expr = union(enum) {
403403
str_is_empty,
404404
str_is_eq,
405405
str_concat,
406+
str_contains,
406407
str_trim,
408+
str_trim_start,
409+
str_trim_end,
407410
str_caseless_ascii_equals,
408411
str_with_ascii_lowercased,
409412
str_with_ascii_uppercased,

src/eval/interpreter.zig

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2818,6 +2818,23 @@ pub const Interpreter = struct {
28182818
out.is_initialized = true;
28192819
return out;
28202820
},
2821+
.str_contains => {
2822+
// Str.contains : Str, Str -> Bool
2823+
std.debug.assert(args.len == 2);
2824+
2825+
const haystack_arg = args[0];
2826+
const needle_arg = args[1];
2827+
2828+
std.debug.assert(haystack_arg.ptr != null);
2829+
std.debug.assert(needle_arg.ptr != null);
2830+
2831+
const haystack: *const RocStr = @ptrCast(@alignCast(haystack_arg.ptr.?));
2832+
const needle: *const RocStr = @ptrCast(@alignCast(needle_arg.ptr.?));
2833+
2834+
const result = builtins.str.strContains(haystack.*, needle.*);
2835+
2836+
return try self.makeBoolValue(result);
2837+
},
28212838
.str_trim => {
28222839
// Str.trim : Str -> Str
28232840
std.debug.assert(args.len == 1);
@@ -2841,6 +2858,52 @@ pub const Interpreter = struct {
28412858
out.is_initialized = true;
28422859
return out;
28432860
},
2861+
.str_trim_start => {
2862+
// Str.trim_start : Str -> Str
2863+
std.debug.assert(args.len == 1);
2864+
2865+
const str_arg = args[0];
2866+
std.debug.assert(str_arg.ptr != null);
2867+
2868+
const roc_str_arg: *const RocStr = @ptrCast(@alignCast(str_arg.ptr.?));
2869+
2870+
const result_str = builtins.str.strTrimStart(roc_str_arg.*, roc_ops);
2871+
2872+
// Allocate space for the result string
2873+
const result_layout = str_arg.layout; // Str layout
2874+
var out = try self.pushRaw(result_layout, 0);
2875+
out.is_initialized = false;
2876+
2877+
// Copy the result string structure to the output
2878+
const result_ptr: *RocStr = @ptrCast(@alignCast(out.ptr.?));
2879+
result_ptr.* = result_str;
2880+
2881+
out.is_initialized = true;
2882+
return out;
2883+
},
2884+
.str_trim_end => {
2885+
// Str.trim_end : Str -> Str
2886+
std.debug.assert(args.len == 1);
2887+
2888+
const str_arg = args[0];
2889+
std.debug.assert(str_arg.ptr != null);
2890+
2891+
const roc_str_arg: *const RocStr = @ptrCast(@alignCast(str_arg.ptr.?));
2892+
2893+
const result_str = builtins.str.strTrimEnd(roc_str_arg.*, roc_ops);
2894+
2895+
// Allocate space for the result string
2896+
const result_layout = str_arg.layout; // Str layout
2897+
var out = try self.pushRaw(result_layout, 0);
2898+
out.is_initialized = false;
2899+
2900+
// Copy the result string structure to the output
2901+
const result_ptr: *RocStr = @ptrCast(@alignCast(out.ptr.?));
2902+
result_ptr.* = result_str;
2903+
2904+
out.is_initialized = true;
2905+
return out;
2906+
},
28442907
.str_caseless_ascii_equals => {
28452908
// Str.caseless_ascii_equals : Str, Str -> Bool
28462909
std.debug.assert(args.len == 2);

src/eval/test/low_level_interp_test.zig

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,69 @@ test "e_low_level_lambda - Str.concat with longer strings" {
276276
try testing.expectEqualStrings("\"This is a longer string that contains about one hundred characters for testing concatenation. This is the second string that also has many characters in it for testing longer string operations.\"", value);
277277
}
278278

279+
test "e_low_level_lambda - Str.contains with substring in middle" {
280+
const src =
281+
\\x = Str.contains("foobarbaz", "bar")
282+
;
283+
const value = try evalModuleAndGetString(src, 0, test_allocator);
284+
defer test_allocator.free(value);
285+
try testing.expectEqualStrings("True", value);
286+
}
287+
288+
test "e_low_level_lambda - Str.contains with non-matching strings" {
289+
const src =
290+
\\x = Str.contains("apple", "orange")
291+
;
292+
const value = try evalModuleAndGetString(src, 0, test_allocator);
293+
defer test_allocator.free(value);
294+
try testing.expectEqualStrings("False", value);
295+
}
296+
297+
test "e_low_level_lambda - Str.contains with empty needle" {
298+
const src =
299+
\\x = Str.contains("anything", "")
300+
;
301+
const value = try evalModuleAndGetString(src, 0, test_allocator);
302+
defer test_allocator.free(value);
303+
try testing.expectEqualStrings("True", value);
304+
}
305+
306+
test "e_low_level_lambda - Str.contains with substring at start" {
307+
const src =
308+
\\x = Str.contains("hello world", "hello")
309+
;
310+
const value = try evalModuleAndGetString(src, 0, test_allocator);
311+
defer test_allocator.free(value);
312+
try testing.expectEqualStrings("True", value);
313+
}
314+
315+
test "e_low_level_lambda - Str.contains with substring at end" {
316+
const src =
317+
\\x = Str.contains("hello world", "world")
318+
;
319+
const value = try evalModuleAndGetString(src, 0, test_allocator);
320+
defer test_allocator.free(value);
321+
try testing.expectEqualStrings("True", value);
322+
}
323+
324+
test "e_low_level_lambda - Str.contains with empty haystack" {
325+
const src =
326+
\\x = Str.contains("", "hello")
327+
;
328+
const value = try evalModuleAndGetString(src, 0, test_allocator);
329+
defer test_allocator.free(value);
330+
try testing.expectEqualStrings("False", value);
331+
}
332+
333+
test "e_low_level_lambda - Str.contains with identical strings" {
334+
const src =
335+
\\x = Str.contains("test", "test")
336+
;
337+
const value = try evalModuleAndGetString(src, 0, test_allocator);
338+
defer test_allocator.free(value);
339+
try testing.expectEqualStrings("True", value);
340+
}
341+
279342
test "e_low_level_lambda - Str.caseless_ascii_equals with equal strings" {
280343
const src =
281344
\\x = Str.caseless_ascii_equals("hello", "hello")
@@ -474,6 +537,60 @@ test "e_low_level_lambda - Str.trim with a non-whitespace string" {
474537
try testing.expectEqualStrings("\"hello\"", value);
475538
}
476539

540+
test "e_low_level_lambda - Str.trim_start with an empty string" {
541+
const src =
542+
\\x = Str.trim_start("")
543+
;
544+
const value = try evalModuleAndGetString(src, 0, test_allocator);
545+
defer test_allocator.free(value);
546+
try testing.expectEqualStrings("\"\"", value);
547+
}
548+
549+
test "e_low_level_lambda - Str.trim_start with a whitespace string" {
550+
const src =
551+
\\x = Str.trim_start(" ")
552+
;
553+
const value = try evalModuleAndGetString(src, 0, test_allocator);
554+
defer test_allocator.free(value);
555+
try testing.expectEqualStrings("\"\"", value);
556+
}
557+
558+
test "e_low_level_lambda - Str.trim_start with a non-whitespace string" {
559+
const src =
560+
\\x = Str.trim_start(" hello ")
561+
;
562+
const value = try evalModuleAndGetString(src, 0, test_allocator);
563+
defer test_allocator.free(value);
564+
try testing.expectEqualStrings("\"hello \"", value);
565+
}
566+
567+
test "e_low_level_lambda - Str.trim_end with an empty string" {
568+
const src =
569+
\\x = Str.trim_end("")
570+
;
571+
const value = try evalModuleAndGetString(src, 0, test_allocator);
572+
defer test_allocator.free(value);
573+
try testing.expectEqualStrings("\"\"", value);
574+
}
575+
576+
test "e_low_level_lambda - Str.trim_end with a whitespace string" {
577+
const src =
578+
\\x = Str.trim_end(" ")
579+
;
580+
const value = try evalModuleAndGetString(src, 0, test_allocator);
581+
defer test_allocator.free(value);
582+
try testing.expectEqualStrings("\"\"", value);
583+
}
584+
585+
test "e_low_level_lambda - Str.trim_end with a non-whitespace string" {
586+
const src =
587+
\\x = Str.trim_end(" hello ")
588+
;
589+
const value = try evalModuleAndGetString(src, 0, test_allocator);
590+
defer test_allocator.free(value);
591+
try testing.expectEqualStrings("\" hello\"", value);
592+
}
593+
477594
test "e_low_level_lambda - List.concat with two non-empty lists" {
478595
const src =
479596
\\x = List.concat([1, 2], [3, 4])
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# META
2+
~~~ini
3+
description=Str.contains should work with various string combinations
4+
type=repl
5+
~~~
6+
# SOURCE
7+
~~~roc
8+
» Str.contains("foobarbaz", "bar")
9+
» Str.contains("apple", "orange")
10+
» Str.contains("anything", "")
11+
» Str.contains("hello world", "hello")
12+
» Str.contains("hello world", "world")
13+
» Str.contains("test", "test")
14+
» Str.contains("", "hello")
15+
~~~
16+
# OUTPUT
17+
True
18+
---
19+
False
20+
---
21+
True
22+
---
23+
True
24+
---
25+
True
26+
---
27+
True
28+
---
29+
False
30+
# PROBLEMS
31+
NIL
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# META
2+
~~~ini
3+
description=Str.trim_end should work with various string combinations
4+
type=repl
5+
~~~
6+
# SOURCE
7+
~~~roc
8+
» Str.trim_end(" Hello")
9+
» Str.trim_end("Hello ")
10+
» Str.trim_end(" Hello World ")
11+
» Str.trim_end("Hello World")
12+
» Str.trim_end(" ")
13+
» Str.trim_end("")
14+
~~~
15+
# OUTPUT
16+
" Hello"
17+
---
18+
"Hello"
19+
---
20+
" Hello World"
21+
---
22+
"Hello World"
23+
---
24+
""
25+
---
26+
""
27+
# PROBLEMS
28+
NIL
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# META
2+
~~~ini
3+
description=Str.trim_start should work with various string combinations
4+
type=repl
5+
~~~
6+
# SOURCE
7+
~~~roc
8+
» Str.trim_start(" Hello")
9+
» Str.trim_start("Hello ")
10+
» Str.trim_start(" Hello World ")
11+
» Str.trim_start("Hello World")
12+
» Str.trim_start(" ")
13+
» Str.trim_start("")
14+
~~~
15+
# OUTPUT
16+
"Hello"
17+
---
18+
"Hello "
19+
---
20+
"Hello World "
21+
---
22+
"Hello World"
23+
---
24+
""
25+
---
26+
""
27+
# PROBLEMS
28+
NIL

0 commit comments

Comments
 (0)