LLuce

Text processing

The language keeps the String primitives; everything else is std.strings, written in ordinary Luce. With the import in scope the method spelling works: s.split(x) is strings.split(s, x).

main.luc
import std.strings

func main():
    let line = "  name , age , city  "
    var fields: List(String) = []
    for raw in line.split(","):
        fields.append(raw.trim())
    print(f"{len(fields)}: [{fields.join("][")}]")
Output
3: [name][age][city]

Searching and testing#

main.luc
import std.strings

func main():
    let path = "src/luce/06_mir/module.zig"
    print(f"find '/': {path.find("/")}")
    print(f"find_from '/' after 4: {path.find_from("/", 4)}")
    print(f"contains 'mir': {path.contains("mir")}")
    print(f"starts_with 'src': {path.starts_with("src")}")
    print(f"ends_with '.zig': {path.ends_with(".zig")}")
    print(f"count '/': {path.count("/")}")
Output
find '/': 3
find_from '/' after 4: 8
contains 'mir': true
starts_with 'src': true
ends_with '.zig': true
count '/': 3

Reshaping#

main.luc
import std.strings

func main():
    let messy = "\tThe Quick   Brown Fox\n"
    print(f"[{messy.trim()}]")
    print(messy.trim().lower())
    print(messy.trim().upper())
    print(messy.trim().replace("Quick", "Slow"))
    print(f"[{"-".repeat(20)}]")
    print(f"[{"42".pad_left(6)}][{"42".pad_right(6)}]")
Output
[The Quick   Brown Fox]
the quick   brown fox
THE QUICK   BROWN FOX
The Slow   Brown Fox
[--------------------]
[    42][42    ]

An empty separator splits on runs of whitespace and drops the empty pieces — Python's split(). A real separator keeps them.

main.luc
import std.strings

func main():
    let messy = "  a   b  c  "
    print(f"whitespace: {len(messy.split(""))} pieces")
    print(f"on space:   {len(messy.split(" "))} pieces")
Output
whitespace: 3 pieces
on space:   10 pieces

Building text#

Repeated + allocates every time; a Builder does not.

main.luc
func main():
    var table = new Builder()
    for row in range(0, 4):
        for column in range(0, 4):
            table.append(str(row * column))
            table.append_ascii(9)      # a tab, without allocating a String
        table.append("\n")
    print(str(table))
Output
0	0	0	0	
0	1	2	3	
0	2	4	6	
0	3	6	9	

Walking bytes#

len(s) is bytes, byte_at(i) reads one, and find_byte(byte, start) scans. Those three are the primitives everything else is built on, and the seam where the runtime may vectorize.

main.luc
func main():
    let text = "a,bb,ccc"
    var start = 0
    var pieces = 0
    while true:
        let comma = text.find_byte(44, start)
        if comma < 0:
            pieces += 1
            print(f"piece: {text[start:len(text)]}")
            break
        pieces += 1
        print(f"piece: {text[start:comma]}")
        start = comma + 1
    print(f"{pieces} pieces, {len(text)} bytes")
Output
piece: a
piece: bb
piece: ccc
3 pieces, 8 bytes

Formatting numbers#

str gives the shortest round-trip form of a Float. strings.format_float(x, decimals) gives fixed point, rounding half away from zero.

main.luc
import std.strings

func main():
    print(str(1.0 / 3.0))
    print(strings.format_float(1.0 / 3.0, 4))
    print(strings.format_float(2.5, 0))
    print(strings.format_float(-2.345, 2))
Output
0.3333333333333333
0.3333
3
-2.35