soma.cell

Caesar cipher — shift letters using an alphabet lookup

Builds the shifted output by finding each character's position in the lowercase alphabet with index_of, rotating by the shift, and indexing back in. Non-letters pass through unchanged. Decrypt = encrypt with (26 - shift). Run: soma run caesar.cell --signal encrypt "hello" 3

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

How it is built

The cell Caesar declares 2 signals in its face: encrypt(text: String, shift: Int) -> String and decrypt(text: String, shift: Int) -> String. Handlers: encrypt (line 17) and decrypt (line 34).

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

Generated from soma describe on this file.

The source

caesar.cell

// ============================================================================
// Caesar cipher — shift letters using an alphabet lookup
// ============================================================================
//
// Builds the shifted output by finding each character's position in the
// lowercase alphabet with index_of, rotating by the shift, and indexing back
// in. Non-letters pass through unchanged. Decrypt = encrypt with (26 - shift).
//
// Run: soma run caesar.cell --signal encrypt "hello" 3

cell Caesar {
    face {
        signal encrypt(text: String, shift: Int) -> String
        signal decrypt(text: String, shift: Int) -> String
    }

    on encrypt(text: String, shift: Int) {
        let alpha = "abcdefghijklmnopqrstuvwxyz"
        let k = ((shift % 26) + 26) % 26
        let low = lowercase(text)
        let out = ""
        for i in range(0, len(low)) {
            let c = low[i]
            let pos = index_of(alpha, c)
            if pos >= 0 {
                out = out + alpha[(pos + k) % 26]
            } else {
                out = out + c
            }
        }
        return out
    }

    on decrypt(text: String, shift: Int) {
        return encrypt(text, 26 - (shift % 26))
    }
}

cell test CaesarTest {
    rules {
        assert encrypt("hello", 3) == "khoor"
        assert encrypt("xyz", 5) == "cde"
        assert decrypt("khoor", 3) == "hello"
        assert encrypt("abc 123", 1) == "bcd 123"
        assert encrypt("hello", 0) == "hello"
        assert decrypt(encrypt("attack at dawn", 13), 13) == "attack at dawn"
    }
}

What the toolchain says

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

$ soma check caesar.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
  --> caesar.cell:26:17
   |
26 |                 out = out + alpha[(pos + k) % 26]
   |                 ^
✓ 1 warning(s), no errors.
$ soma test caesar.cell
test CaesarTest ...
  ✓ assert encrypt("hello", 3) == "khoor"
  ✓ assert encrypt("xyz", 5) == "cde"
  ✓ assert decrypt("khoor", 3) == "hello"
  ✓ assert encrypt("abc 123", 1) == "bcd 123"
  ✓ assert encrypt("hello", 0) == "hello"
  ✓ assert decrypt(encrypt("attack at dawn", 13), 13) == "attack at dawn"

6 tests: 6 passed, 0 failed
$ soma verify caesar.cell
Verifying caesar.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

index_of, len, lowercase, range

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