Strings and Chunk Expressions
Lingo string handling - chunk expressions (char, word, item, line), itemDelimiter, searching, mutation, and parsing binary-delimited network data.
String fundamentals
s = "Hello, World"
put s.length -- 12 (also: the length of s, length(s))
put s & "!" -- concatenation
put "a" && "b" -- "a b" (concatenate with a space)
put EMPTY -- ""- Strings are 1-indexed sequences of single-byte characters (pre-Director 11).
- Comparison operators (
=,<,contains,starts) are case-insensitive. offset("lo", s)returns the 1-based position of a substring (0 if absent) and IS case-insensitive like the rest.
put "HELLO" = "hello" -- 1 (TRUE)
put s contains "WORLD" -- 1
put s starts "hel" -- 1
put offset("world", s) -- 8Chunk expressions
Chunks are Lingo's signature feature: addressing substrings structurally.
| Chunk | Separator rule |
|---|---|
char n of s | Single characters. |
word n of s | Runs separated by spaces, tabs, and returns (any run of whitespace counts as one separator). |
item n of s | Separated by the itemDelimiter (default comma). |
line n of s | Separated by RETURN (carriage return, char 13). |
t = "red,green,blue"
put item 2 of t -- "green"
put char 1 of t -- "r"
put char 1 to 3 of t -- "red"
put word 2 of "the quick fox" -- "quick"
put line 2 of ("a" & RETURN & "b") -- "b"
put the number of chars in t -- 14
put the number of items in t -- 3
put the number of words in t -- 1
put the number of lines in t -- 1
put t.item[2] -- dot-syntax chunk access (D7+)
put t.item[1..2] -- "red,green" (range keeps delimiters)Chunks nest arbitrarily:
put word 1 of item 2 of line 3 of bigTextOut-of-range chunk reads return EMPTY, never an error. the last word of s, the last char of s etc. are supported, as is counting via the number of words in s.
the itemDelimiter
the itemDelimiter = TAB
fields = the number of items in row
name = item 2 of row
the itemDelimiter = "," -- restore!- It is a single character, global to the movie, and anything that parses items while you have it changed will use your value. Classic bugs come from a handler changing it and not restoring it.
- MX 2004 documents only a single-character delimiter.
- Habbo-era network code sets it to control characters like
numToChar(2)to split server packets.
Chunk mutation
Chunks are assignable targets, and put ... into/before/after works on them:
s = "red,green,blue"
put "GREEN" into item 2 of s -- "red,GREEN,blue"
put ">" before word 1 of s
delete item 3 of s -- removes the chunk AND its delimiterMutating chunk expressions on field members edits the visible text directly (put "hi" into line 2 of field "chat"), which is how classic UIs updated text.
Related field/text utilities:
hilite word 2 of field "doc" -- select text in a field
put chars(s, 2, 4) -- "edg" (substring function form)Search and split idioms
Lingo has no split(); real code splits with chunks:
on splitString input, delim
saved = the itemDelimiter
the itemDelimiter = delim
parts = []
cnt = the number of items in input
repeat with i = 1 to cnt
add parts, item i of input
end repeat
the itemDelimiter = saved
return parts
endoffset() powers streaming parsers:
pos = offset(delim, buffer)
if pos > 0 then
packet = char 1 to pos - 1 of buffer
delete char 1 to pos of buffer
end ifCase conversion and formatting
Classic Lingo (through MX 2004) has no built-in case conversion; code ships its own toUpper/toLower via charToNum/numToChar loops. Number formatting goes through string(), the floatPrecision, and manual padding.
Parsing binary-delimited protocols
Multiplayer-era Shockwave games (Habbo Hotel being the canonical example) transported data as text with control-character separators:
chr(1)/numToChar(1)between logical records,chr(2)between fields,chr(13)(RETURN) as an older record separator, which conveniently makes records addressable asline n of packet.
Rules that emulators must match exactly:
wordsplitting treats space, TAB, and RETURN as separators. Control characters likechr(1)/chr(2)are not word separators; they stay glued to adjacent characters, soword 1 of ("a" & numToChar(2) & "b")is the whole 3-character string.linesplits only on RETURN (char 13);chr(10)(linefeed) is not a line separator in classic Mac-heritage Lingo, though Windows CRLF text yields a straychr(10)at line starts that robust code strips.- Numeric coercion of a field that still carries a trailing delimiter (
integer("42" & numToChar(2))) returns VOID under MX 2004's strict rule; game code therefore isolates the digits first (chunk off the delimiter, then coerce). When emulating older players, test this exact case against the target version before assuming leniency. value()on delimited text is even stricter and also side-effect-prone; avoid it in protocol paths.
-- typical packet field extraction
the itemDelimiter = numToChar(2)
userId = integer(item 1 of packet)
userName = item 2 of packetString performance
Strings are immutable values; put ... after bigString inside a tight loop is O(n^2). Old well-written Lingo accumulates into fields or lists and joins once; emulators should still expect and tolerate the slow pattern.