Skip to content

Commit b61151e

Browse files
committed
Merge remote-tracking branch 'origin/main' into propagate_is_eq
2 parents 0884928 + df66010 commit b61151e

File tree

7 files changed

+121
-0
lines changed

7 files changed

+121
-0
lines changed

src/build/builtin_compiler/main.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ 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
}

src/build/roc/Builtin.roc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ Builtin :: [].{
22
Str :: [ProvidedByCompiler].{
33
is_empty : Str -> Bool
44
concat : Str, Str -> Str
5+
contains : Str, Str -> Bool
56
trim : Str -> Str
67
caseless_ascii_equals : Str, Str -> Bool
78
with_ascii_lowercased : 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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ pub const Expr = union(enum) {
403403
str_is_empty,
404404
str_is_eq,
405405
str_concat,
406+
str_contains,
406407
str_trim,
407408
str_caseless_ascii_equals,
408409
str_with_ascii_lowercased,

src/eval/interpreter.zig

Lines changed: 17 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);

src/eval/test/low_level_interp_test.zig

Lines changed: 63 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")
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

0 commit comments

Comments
 (0)