soma.cell

Palindrome check — punctuation- and case-insensitive

Strips everything but letters/digits, lowercases, then compares the cleaned string against its reverse. Demonstrates per-character filtering with s[i] and a descending range to build the reverse. Run: soma run palindrome.cell --signal is_palindrome "A man, a plan, a canal: Panama"

A Soma program from the text part of the verified corpus: 54 lines, cells Palindrome, PalindromeTest. It passes soma check with soma 3.2.4.

How it is built

The cell Palindrome declares 2 signals in its face: is_palindrome(s: String) -> Bool and reverse_str(s: String) -> String. Handlers: reverse_str (line 30) and is_palindrome (line 38). Private helpers: _clean.

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

Generated from soma describe on this file.

The source

palindrome.cell

// ============================================================================
// Palindrome check — punctuation- and case-insensitive
// ============================================================================
//
// Strips everything but letters/digits, lowercases, then compares the cleaned
// string against its reverse. Demonstrates per-character filtering with s[i]
// and a descending range to build the reverse.
//
// Run: soma run palindrome.cell --signal is_palindrome "A man, a plan, a canal: Panama"

cell Palindrome {
    face {
        signal is_palindrome(s: String) -> Bool
        signal reverse_str(s: String) -> String
    }

    // Keep only alphanumeric characters, lowercased.
    on _clean(s: String) {
        let low = lowercase(s)
        let out = ""
        for i in range(0, len(low)) {
            let c = low[i]
            if (c >= "a" && c <= "z") || (c >= "0" && c <= "9") {
                out = out + c
            }
        }
        return out
    }

    on reverse_str(s: String) {
        let out = ""
        for i in range(len(s) - 1, -1, -1) {
            out = out + s[i]
        }
        return out
    }

    on is_palindrome(s: String) {
        let c = _clean(s)
        return c == reverse_str(c)
    }
}

cell test PalindromeTest {
    rules {
        assert is_palindrome("racecar")
        assert is_palindrome("A man, a plan, a canal: Panama")
        assert is_palindrome("Was it a car or a cat I saw?")
        assert !is_palindrome("hello")
        assert reverse_str("abc") == "cba"
        assert is_palindrome("12321")
    }
}

What the toolchain says

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

$ soma check palindrome.cell
warning: `out` grows by concatenation inside a loop — each step copies the whole value, so the loop is quadratic (8 MB took 32 s, a list + join() 30 ms): push the pieces to a List and `join(parts, "")` once
  --> palindrome.cell:24:17
   |
24 |                 out = out + c
   |                 ^
warning: `out` grows by concatenation inside a loop — each step copies the whole value, so the loop is quadratic (8 MB took 32 s, a list + join() 30 ms): push the pieces to a List and `join(parts, "")` once
  --> palindrome.cell:33:13
   |
33 |             out = out + s[i]
   |             ^
✓ 2 warning(s), no errors.
$ soma test palindrome.cell
test PalindromeTest ...
  ✓ assert is_palindrome("racecar")
  ✓ assert is_palindrome("A man, a plan, a canal: Panama")
  ✓ assert is_palindrome("Was it a car or a cat I saw?")
  ✓ assert !is_palindrome("hello")
  ✓ assert reverse_str("abc") == "cba"
  ✓ assert is_palindrome("12321")

6 tests: 6 passed, 0 failed
$ soma verify palindrome.cell
Verifying palindrome.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
VERIFY OK (vacuous — no state machine)

Builtins used

len, lowercase, range

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