Skip to content

Strings and Characters ​

Implementation status: Implemented (UTF-8 storage, grapheme-cluster counting Phase 84, for-in, .count, subscripts, split/trim/... and all other stdlib methods, regex literals Phase 50+); Character is a Phase 139 MVP GA scalar type distinct from String (not a complete Unicode Character property library)

Strings are one of the areas where SYX differs most from Swift. This chapter explains the four key points: "grapheme clusters vs. scalars", "the Character type", "interpolation and escapes", and "regex literals".

Creating Strings ​

swift
let s = "Hello"
let name = "World"
let msg = "Hello, \(name)!"        // Interpolation
let multi = "a" + "b" + "c"        // String concatenation
let multiline = "line one\nline two"
let tabbed = "col1\tcol2\tcol3"

Sources: crates/syx-driver/tests/fixtures/lang/string_multiline.sy · examples/main.sy

String Literal Syntax ​

FormSyntaxDescription
Single line"…"Delimited by double quotes; supports escapes and interpolation
MultilineMVP GA"""…""" may contain raw newlines and \(expr) interpolation (Phase 139)
Escapes\n \t \\ \" \(expr)Same as Swift
Interpolation\(expr)Arbitrary expressions (function calls, arithmetic, field access)
swift
let name = "World"
let count = 3
let s1 = "Hello, \(name)!"
let s2 = "count=\(count + 1)"        // Expression
let s3 = "items: \([1,2,3].count)"  // Any expression is legal

Important: raw newlines are not allowed inside a single-line string (L001). Multiline """ literals allow raw newlines (Phase 139 MVP).

Escape Sequences ​

EscapeMeaningByte
\nNewline (LF)0x0A
\rCarriage return (CR)0x0D
\tTab0x09
\\Backslash0x5C
\"Double quote0x22
\'Single quote0x27
\0Null character (NUL)0x00
\(…)Interpolation (no 0 byte; compiled to StringConcat)—
\x41ASCII byte0x41
\u{1F600}Unicode code pointemoji 0x1F600
swift
let s = "tab\there\nnewline"
let emoji = "\u{1F600}"            // 😀
let zwnj = "\u{200C}"              // ZWNJ (zero-width non-joiner)

Common String Methods ​

swift
"a,b,c".split(",")                       // ["a", "b", "c"]
"  hi  ".trim()                          // "hi"
"ABC".lowercased()                       // "abc"
"abc".uppercased()                       // "ABC"
"file.txt".hasPrefix("file")             // true
"hello.txt".hasSuffix(".txt")            // true
"hello".substring(0, 3)                  // "hel"  [start, end) scalar index
"banana".replacingOccurrences("a", "o")  // "bonono"
"abc123".matches("\\d+")                 // ["123"] (regex matching returns an array of substrings)
"text".count                            // 4 (number of grapheme clusters)
"text".isEmpty                           // false
"  ".isEmpty                             // false (a space is also a grapheme cluster)

The complete String method registry lives in crates/syx-stdlib/src/string_methods.rs (STRING_METHODS). examples/main.sy gives an end-to-end example.

MethodSignatureDescription
split(sep: String)Array<String>Split by separator
trim()StringStrip leading/trailing Unicode whitespace
lowercased()StringUnicode simple case mapping (Rust; ≠ caseFolded; ≠ locale)
uppercased()StringUnicode simple case mapping (Rust; ≠ caseFolded; ≠ locale)
normalizedNFC(text:)StringUnicode NFC (ADR-228)
normalizedNFD(text:)StringUnicode NFD (ADR-228)
caseFolded(text:)StringUnicode default/full case folding (e.g. ß→ss; ADR-228)
hasPrefix(s)BoolPrefix test
hasSuffix(s)BoolSuffix test
substring(start, end)StringScalar substring [start, end)
replacingOccurrences(target, with)StringReplacement (first / all depending on the implementation)
matches(pattern)Array<String>Regex matching
countIntNumber of grapheme clusters (not bytes)
isEmptyBoolZero grapheme clusters
repeat(n)StringRepeated n times

Iterating Over Strings ​

swift
for c in "abc" {
    print(c)    // Prints "a", "b", "c" in turn
}

The loop variable c has type Character (since Phase 139 a distinct scalar type; at runtime it is not a multi-character String).

Implementation status: for-in is Implemented; the loop variable is Character (MVP GA; fixtures/lang/character.sy).

Internal Representation (Important) ​

LayerSYX
StorageUTF-8 byte array (Rust String back end)
.count / substring / for-inUnicode grapheme clusters (since Phase 84, indexed with unicode-segmentation)
Character typeMVP GA (Phase 139; runtime Value::Character scalar; not a complete Unicode property library)
Character.isASCII()MVP GA (single-scalar check; not a complete ASCII property judgment)

Swift alignment: combined emoji (e.g. 👨‍👩‍👧‍👦, a family) count as 1 "character" in Swift; under the Phase 84 grapheme-cluster index, SYX also counts 1.

Legacy markers: before Phase 84, .count counted Unicode scalars; old fixtures (stdlib/string_index_honesty.rs and others) keep scalar behavior tests. If .count disagrees with earlier documentation, trust the current behavior.

Subscripts s[i] ​

swift
let s = "syx"
let c: Character = s[0]      // Grapheme Character
print(c)                     // "s"

let second = s[1]            // "w"
BehaviorSYX
Index semanticsInt grapheme offset (Phase 84; ADR-227)
ComplexityO(n) for a single random access (no opaque Index / no O(1) cache)
Return typeCharacter (grapheme cluster)
Out of boundsRuntime error R003 (message contains out of bounds; ≠ an empty Character; ≠ R013)
vs. Swift≠ opaque String.Index; SYX uses integer offsets

Range access: the slicing syntax s[0..<3] is not yet provided; use substring(0, 3) (also an O(n) grapheme offset). Pinning test: m2_s2_02_string_index_honesty.

String Concatenation ​

swift
let a = "Hello"
let b = ", World"
let c = a + b                       // "Hello, World"
let d = "\(a)\(b)!"                 // Concatenation via interpolation
let e = a + b + "!"                 // Same as joining fragments with +

The + operator on String is the low-level lowering of the StringConcat instruction (not a Core Host function); at runtime it is inlined into a byte copy.

String Comparison ​

swift
"abc" == "abc"     // true (value equality, Equatable witness)
"abc" < "abd"      // true (lexicographic)
"abc" === "abc"    // false (different String objects; === compares references)

Comparison semantics: == compares grapheme-cluster sequences (since Phase 84); < is lexicographic (Comparable witness). === compares object identity at the VM/MIR layer (rarely used).

Regex Literals ​

swift
let matches = "abc123def".matches("\\d+")   // Pattern string (regex literal `/…/` MIR still partial)
for m in matches { print(m) }               // "123"

let helloHits = "Hello hello".matches("hello")
print(helloHits.count)
FormStatusDescription
/pattern/ImplementedParsed by crates/syx-regex; disambiguated by context
/pattern/iPartially implementedThe flag is accepted by the parser; runtime support is limited
/pattern/m /pattern/sNot implementedMultiline / single-line flags

Disambiguation: / does not occur at the start of a comment (line comments use //), but it can be ambiguous in division expressions (x/y vs. a regex). The parser distinguishes them by token lookahead; typical cases need no attention.

See Language Reference: Lexical Structure.

Converting Between Strings and Collections ​

swift
let s = "a,b,c"
let parts = s.split(",")                  // ["a", "b", "c"]
let joined = parts.joined("-")            // "a-b-c"

var graphemes: Array<String> = []
for ch in s {
    graphemes.append(ch)                  // Grapheme clusters via for-in
}
let back = graphemes.joined("")           // "a,b,c"
DirectionMethod
String → Array<String>s.split(sep)
Array<String> → Stringarr.joined(sep)
String → grapheme listfor ch in s (append into Array)
Grapheme list → Stringgraphemes.joined("")

Complete Example ​

Source fixtures/lang/string_multiline.sy:

swift
func main() {
    let name = "SYX"
    print("Hello, \(name)!")

    print("  Hello, World  ".trim())

    let parts = "a,b,c".split(",")
    for item in parts {
        print(item)
    }

    for m in "abc123def".matches("\\d+") {
        print(m)
    }

    var digits: Array<String> = []
    for ch in "0123456789" {
        digits.append(ch)
    }
    print(digits.count)              // 10
    print(digits[0])                  // "0"
}

Internal APIs (Core Extensions) ​

The String witnesses (hash / description / debugDescription) are implemented through Core extensions → Host functions (see Chapter 32, Operators vs Core Host):

swift
extension String {
    func hash(into hasher: Hasher) {
        __syx_hasher_combine(hasher, self)
    }
    func description() -> String {
        return __syx_value_description(self)
    }
}

String is fully registered in crates/syx-stdlib/src/builtins/string.sy; user code generally need not call __syx_* directly.

Diagnostics ​

CodeTriggerFix
L001Unterminated stringClose the "
R003s[i] out of bounds (including negative indices)Check the index; ≠ an empty Character
S004Using String and Array interchangeablyUse an explicit .split() / .joined()

Differences from Swift ​

SwiftSYX
Character is a standalone structCharacter is a String type alias (a single runtime scalar)
opaque String.IndexInt grapheme offset (O(n) random access; ≠ opaque Index)
s[i] returns CharacterReturns a grapheme Character (ADR-226/227)
s[range] slicings.substring(start, end)
String(format:)Not implemented; use interpolation or Int.toString()
count.count (consistent)
String == StringGrapheme-cluster sequence equality (since Phase 84)
String literal interpolationCompatible with Swift's \(expr) syntax

Next chapter → Collection Types

See also → Language Reference: Lexical Structure · Chapter 5, Collection Types · Chapter 32, Operators vs Core Host

SYX · Apache-2.0