DDirectorWikiDirector & Lingo Encyclopedia

Data Types and Coercion

Lingo's value types - integers, floats, strings, symbols, VOID, points, rects, colors - plus type predicates and conversion rules.

Type inventory

TypeLiteral / constructorilk() symbolNotes
Integer42, -7#integerSigned 32-bit. Overflow wraps.
Float3.14, 1e6, float(x)#floatDouble precision internally; display digits governed by the floatPrecision.
String"hello"#string1-indexed; case-insensitive comparisons.
Symbol#idle, symbol("idle")#symbolInterned name; constant-time equality; case-insensitive.
BooleanTRUE / FALSE#integerJust integers 1 and 0. Any nonzero integer is true in conditions.
VOIDVOID#voidThe empty value; returned for missing things. Test with voidP().
List[1, 2, 3]#listLinear list; see Lists.
Property list[#a: 1, #b: 2]#propListOrdered key/value pairs. Empty literal is [:].
Pointpoint(10, 20)#pointp.locH, p.locV; supports arithmetic.
Rectrect(l, t, r, b)#rectAlso rect(point1, point2); r.left/top/right/bottom/width/height.
Colorrgb(255, 0, 0), rgb("#FF0000"), paletteIndex(35)#colorTwo color models: direct RGB and palette index.
Datedate(2004, 1, 31)#dateMX 2004 date object.
Vectorvector(x, y, z)#vector3D vector for Shockwave 3D.
Imageimage(w, h, depth), member(m).image#imageImaging Lingo bitmap surface (D8+).
Objectnew(script "Foo")#instance / #objectChild object, behavior instance, Xtra instance, window, etc.
Member/Sprite refsmember(1), sprite(1)#member, #spriteReferences, not copies.

Constants

ConstantValue
TRUE / FALSE1 / 0
VOIDempty value
EMPTY"" (empty string)
RETURNcarriage return character (char 13)
ENTERnumeric-keypad Enter (char 3 on classic Mac mapping)
TABtab character (char 9)
BACKSPACEbackspace character (char 8)
QUOTEthe " character (the only way to embed quotes in a string literal)
SPACE" "
PI3.14159265...
lingo
put "She said" && QUOTE & "hi" & QUOTE   -- She said "hi"
line1 = "a" & RETURN & "b"

Type predicates

lingo
integerP(x)   floatP(x)   stringP(x)   symbolP(x)
listP(x)      objectP(x)  voidP(x)     pictureP(x)
ilk(x)              -- returns the type symbol, e.g. #propList
ilk(x, #list)       -- TRUE if x is that ilk (lists: #list matches both kinds)

listP() is TRUE for linear lists, property lists, rects, and points. objectP() is TRUE for anything object-like and FALSE for VOID; old code frequently uses objectP(gConn) as an "is initialized" test.

Truthiness

A condition expects an integer:

  • Integer 0 is FALSE, any other integer is TRUE.
  • TRUE/FALSE are literally 1/0, and the ... of boolean properties return 1/0.
  • Strings, VOID, floats, and lists are not silently truthy: if "abc" then raises a type error in classic Lingo. Emulators must not adopt JavaScript truthiness. Comparisons produce integers, so if x > 0 then is the normal shape.

Numeric coercion

lingo
integer(3.7)     -- 4 (rounds to nearest, .5 rounds away from zero)
integer("42")    -- 42
integer("42x")   -- VOID in MX 2004 (strict trailing-garbage rule; D6 returned 0)
float("3.5")     -- 3.5000
float(7)         -- 7.0000
string(3.5)      -- "3.5000" (uses the floatPrecision, default 4)
value("3 + 4")   -- 7 : value() EVALUATES the string as a Lingo expression
value("[1,2]")   -- the list [1, 2]
value("abc")     -- VOID (not a valid expression)

Behavior worth memorizing:

  • Integer arithmetic stays integer (5 / 2 = 2); introduce a float to promote (5 / 2.0 = 2.5).
  • the floatPrecision (default 4) affects only string formatting, not stored precision.
  • value() is a full expression evaluator, which makes it powerful and dangerous: it resolves symbols and can execute function-like expressions. For parsing server data, well-written code uses integer()/float() instead.
  • String-to-number coercion tolerates leading/trailing spaces. Embedded control characters (like the chr(1)/chr(2) delimiters in Habbo-style network protocols) make MX 2004 integer()/float() return VOID rather than parsing a prefix; robust packet parsers strip delimiters before coercion. Older players were more lenient, so verify against the version you target (see Strings and chunks).

Character coercion

lingo
charToNum("A")     -- 65
numToChar(65)      -- "A"
chr(65)            -- "A" (legacy alias seen in old code)

Classic Director strings are single-byte (MacRoman on Mac-authored, Windows-1252 on Windows-authored content); Unicode arrived only in Director 11. Byte-oriented protocol code (numToChar(1) as a packet separator) relies on this.

Symbols

lingo
state = #idle
if state = #idle then state = #running
put string(#idle)      -- "idle"
put symbol("idle")     -- #idle
  • Symbols are interned once and compared by identity, making them the standard choice for enums, message names (sendSprite(5, #reset)), and property list keys.
  • A symbol must start with a letter; symbol() converts arbitrary strings (invalid ones return VOID).
  • Symbol comparisons are case-insensitive (#Idle = #idle is TRUE).

Points, rects, colors

lingo
p = point(10, 20)
p = p + point(5, 5)             -- point(15, 25); arithmetic is element-wise
r = rect(0, 0, 100, 50)
put r.width                     -- 100
inflated = r + rect(-2,-2,2,2)  -- grow by 2px each side
put inside(p, r)                -- TRUE/FALSE
u = union(r1, r2)
x = intersect(r1, r2)           -- rect(0,0,0,0) when disjoint
m = map(r, fromRect, toRect)    -- proportional rect/point mapping

c = rgb(255, 128, 0)
put c.red, c.green, c.blue
i = paletteIndex(35)            -- palette-relative color

Palette-index colors matter for old 8-bit content: a paletteIndex color resolves through the active palette at draw time, so the same sprite can change color when the frame palette changes. Emulators must keep the distinction between rgb() and paletteIndex() colors instead of flattening both to RGB at parse time (see Native rendering for how the native compositor preserves index provenance).