soma.cell

Substring search — count and locate occurrences

Counts non-overlapping occurrences of a needle by repeatedly slicing past each match, and reports all start indices. Demonstrates index_of with a moving window via substring, and guarding against an empty needle. Run: soma run substring_search.cell --signal count_occurrences "ababab" "ab"

A Soma program from the text part of the verified corpus: 49 lines, cells SubstringSearch, SubstringSearchTest. It passes soma check with soma 3.2.4.

How it is built

The cell SubstringSearch declares 2 signals in its face: count_occurrences(haystack: String, needle: String) -> Int and first_index(haystack: String, needle: String) -> Int. Handlers: count_occurrences (line 17) and first_index (line 34).

The file also holds 1 test cell (SubstringSearchTest) with 6 assertions, run by soma test.

Generated from soma describe on this file.

The source

substring_search.cell

// ============================================================================
// Substring search — count and locate occurrences
// ============================================================================
//
// Counts non-overlapping occurrences of a needle by repeatedly slicing past
// each match, and reports all start indices. Demonstrates index_of with a
// moving window via substring, and guarding against an empty needle.
//
// Run: soma run substring_search.cell --signal count_occurrences "ababab" "ab"

cell SubstringSearch {
    face {
        signal count_occurrences(haystack: String, needle: String) -> Int
        signal first_index(haystack: String, needle: String) -> Int
    }

    on count_occurrences(haystack: String, needle: String) {
        if len(needle) == 0 { return 0 }
        let count = 0
        let i = 0
        let n = len(haystack)
        let m = len(needle)
        while i <= n - m {
            if substring(haystack, i, i + m) == needle {
                count = count + 1
                i = i + m
            } else {
                i = i + 1
            }
        }
        return count
    }

    on first_index(haystack: String, needle: String) {
        return index_of(haystack, needle)
    }
}

cell test SubstringSearchTest {
    rules {
        assert count_occurrences("ababab", "ab") == 3
        assert count_occurrences("aaaa", "aa") == 2
        assert count_occurrences("hello", "z") == 0
        assert count_occurrences("mississippi", "ss") == 2
        assert first_index("hello", "llo") == 2
        assert first_index("hello", "z") == -1
    }
}

What the toolchain says

Run on a fresh copy of the program at build time; the outputs are pasted unedited.

$ soma check substring_search.cell
✓ All checks passed.
$ soma test substring_search.cell
test SubstringSearchTest ...
  ✓ assert count_occurrences("ababab", "ab") == 3
  ✓ assert count_occurrences("aaaa", "aa") == 2
  ✓ assert count_occurrences("hello", "z") == 0
  ✓ assert count_occurrences("mississippi", "ss") == 2
  ✓ assert first_index("hello", "llo") == 2
  ✓ assert first_index("hello", "z") == -1

6 tests: 6 passed, 0 failed
$ soma verify substring_search.cell
Verifying substring_search.cell...

SubstringSearch/termination:

  ⚠ handler `count_occurrences`: while-loop without provable termination bound (write `while [loop_bound(N)] cond { … }`, or a for-loop over a bounded range)

0 passed, 1 warnings, 0 failures
Temporal: 0 passed, 0 failed
VERIFY OK

Builtins used

index_of, len, substring

Source file: /corpus/text/substring_search.cell · examples/corpus/text/substring_search.cell