examples/wrap/
Wrap text to a chosen line width and test the boundary cases.
- 171 lines
The tests cover words longer than a line, spaces at a line break and empty input. Each case checks where a small counting error could add or lose a character.
Run it
The quick brown fox
jumps over the lazy
dog and keeps going
for a while
4
19
a
supercalifragilistic
b
20
ab cd
ab
cd
0
true
exit 0 · 15 lines
The program
# Text folded to a width — the twentieth program, and the one whose edges are
# all off by one.
#
# Corpus and regression material (CLAUDE.md §9). It closes no measured form
# gap. It is here because wrapping is the smallest job whose every boundary is
# a decision somebody gets wrong:
#
# - a word that fits EXACTLY on the line: does the space go before it?
# - a word longer than the whole width: break it, or overflow the line?
# - two spaces between words: kept, or collapsed?
# - a trailing space at a break: on the line above, or dropped?
#
# There is no right answer to any of them, which is why they are all named in
# the module doc and all asserted below. A wrapper that answers them silently
# is a wrapper whose output nobody can predict, and predicting the output is
# the entire purpose of wrapping text.
#
# THIS ONE ANSWERS: a word that fits exactly stays on the line; a word longer
# than the width OVERFLOWS rather than being broken, because breaking a URL
# or an identifier is worse than a long line; runs of whitespace collapse to
# one space, because the input's spacing is not the output's; and a trailing
# space is never written, because it is invisible and it changes a diff.
function words_of(text: str) -> [str]
out: [str] @= []
piece: str @= ""
for ch in text.chars()
if ch == " " || ch == "\n" || ch == "\t"
if piece.len() > 0
out @ out.push(piece)
piece @ ""
else
piece @ piece + ch
if piece.len() > 0
out @ out.push(piece)
return out
function wrapped(text: str, width: i64) -> [str]?
if width < 1
return fail(code: "bad_width", msg: "a width below one leaves nowhere to put a word")
lines: [str] @= []
current: str @= ""
for word in words_of(text)
if current.len() == 0
# First word on the line, whatever its length: a word longer than
# the width overflows rather than being broken.
current @ word
continue
# `+ 1` for the space that would join them. This is the off-by-one
# every wrapper has, and `<=` rather than `<` is what makes a word
# that fits EXACTLY stay on the line.
if current.len() + 1 + word.len() <= width
current @ current + " " + word
else
lines @ lines.push(current)
current @ word
if current.len() > 0
lines @ lines.push(current)
return ok(lines)
function rendered(text: str, width: i64) -> str?
return ok(join(wrapped(text, width: width)?, "\n"))
# The longest line the wrapper produced, which is the number a caller checks
# against the width it asked for — and which may EXCEED it, by exactly as much
# as the longest single word does.
function longest(text: str, width: i64) -> i64?
most: i64 @= 0
for line in wrapped(text, width: width)?
if line.len() > most
most @ line.len()
return ok(most)
constant PASSAGE: str
"The quick brown fox jumps over the lazy dog and keeps going for a while"
function main()
print(rendered(PASSAGE, width: 20).must())
print(wrapped(PASSAGE, width: 20).must().len())
print(longest(PASSAGE, width: 20).must())
# A word longer than the width overflows, and the wrapper says how far.
print(rendered("a supercalifragilistic b", width: 10).must())
print(longest("a supercalifragilistic b", width: 10).must())
# The exact fit, which is where `<=` and `<` disagree.
print(rendered("ab cd", width: 5).must())
print(rendered("ab cd", width: 4).must())
print(wrapped("", width: 10).must().len())
print(wrapped("x", width: 0).is_err())
test "a passage folds at the width and every line is inside it"
said = wrapped(PASSAGE, width: 20).must()
assert said.len() == 4
for line in said
assert line.len() <= 20
assert said[0] == "The quick brown fox"
test "a word that fits exactly stays on the line"
# `ab cd` is five characters with its space. At width 5 it is one line;
# at 4 it is two. That pair is the whole difference between `<=` and `<`.
assert rendered("ab cd", width: 5).must() == "ab cd"
assert rendered("ab cd", width: 4).must() == "ab\ncd"
assert rendered("abc de", width: 6).must() == "abc de"
assert rendered("abc de", width: 5).must() == "abc\nde"
test "a word longer than the width overflows rather than being broken"
# Breaking a URL or an identifier is worse than a long line, so the line
# is allowed to exceed the width — and `longest` is how a caller finds out.
said = wrapped("a supercalifragilistic b", width: 10).must()
assert said.len() == 3
assert said[1] == "supercalifragilistic"
assert longest("a supercalifragilistic b", width: 10).must() == 20
test "runs of whitespace collapse to one space"
assert rendered("a b", width: 10).must() == "a b"
assert rendered("a\nb", width: 10).must() == "a b"
assert rendered("a\tb", width: 10).must() == "a b"
assert rendered(" a b ", width: 10).must() == "a b"
test "no line ever ends in a space"
said = wrapped(PASSAGE, width: 12).must()
for line in said
assert line.len() > 0
assert line[line.len() - 1] != ' '
test "an empty input wraps to nothing, not to one empty line"
assert wrapped("", width: 10).must().len() == 0
assert wrapped(" ", width: 10).must().len() == 0
assert rendered("", width: 10).must() == ""
test "a width below one is refused rather than looping"
assert wrapped("x", width: 0).is_err()
assert wrapped("x", width: 0 - 1).is_err()
assert !wrapped("x", width: 1).is_err()
test "a width of one puts every word on its own line"
assert rendered("a b c", width: 1).must() == "a\nb\nc"
assert rendered("ab cd", width: 1).must() == "ab\ncd"
test "wrapping is idempotent at the same width"
# The property a reader assumes and nobody asserts: folding text that is
# already folded changes nothing, because the newline is whitespace and
# the words are the same words.
once = rendered(PASSAGE, width: 20).must()
twice = rendered(once, width: 20).must()
assert once == twice
test "every word survives, in order, whatever the width"
for width in [1, 5, 12, 20, 100]
said = wrapped(PASSAGE, width: width).must()
back: [str] @= []
for line in said
for word in words_of(line)
back @ back.push(word)
assert back == words_of(PASSAGE)
fingerprint sha256 4190b1afc676 · 2 files · index.json
Next: sieve. All the examples. ← Previous: words
Run one yourself: the four commands. Learn the language, or read the full specification in one file.