DDirectorWikiDirector & Lingo Encyclopedia

Variables, Operators, and Control Flow

Local, global, and property variables; Lingo operators and precedence; if, case, and repeat forms.

Variable kinds and scope

KindDeclared withLifetimeVisible to
LocalFirst assignmentOne handler invocationThat handler only
Globalglobal gNameEntire player session, across movie branchesEvery handler that declares the same global
Propertyproperty pNameLife of the script instance / child objectHandlers of that instance (and via the pName of obj)
lingo
global gScore

on prepareMovie
  gScore = 0          -- initialize globals early
end

property pSpeed        -- per-instance state in a behavior/parent script

on new me, aSpeed
  pSpeed = aSpeed
  return me
end

Key semantics:

  • A variable used before assignment raises the classic error "Variable used before assigned a value". In practice this error usually means a missing the before a system property name, or a typo.
  • global must be declared in every handler (or once at script top) that touches the global. Undeclared identifiers become locals.
  • Globals survive go to movie; they are only reset by clearGlobals() or player restart. clearGlobals() also clears the actorList in some player versions, a classic compatibility trap.
  • Property variables on behaviors are per sprite instance; two sprites sharing a behavior get independent copies.
  • showGlobals() and showLocals() print state to the Message window.

Assignment forms

All of these assign; all appear in real code:

lingo
set x = 5
set x to 5
put 5 into x
x = 5

put ... after and put ... before are string append/prepend operators, not general assignment:

lingo
s = "world"
put "hello " before s   -- "hello world"
put "!" after s         -- "hello world!"

Operators

Precedence from highest to lowest (operators on one row bind equally, left to right):

LevelOperators
1( ) parentheses
2unary -, not
3*, /, mod
4+, -
5&, && (string concatenation; && inserts a space)
6=, <>, <, <=, >, >=, contains, starts
7and
8or

Details that matter:

  • = is comparison in expressions and assignment in statements; there is no ==.
  • <> is "not equal".
  • Integer division truncates: 5 / 2 is 2; 5.0 / 2 is 2.5. Mixed integer/float arithmetic promotes to float.
  • mod keeps the sign of the left operand: -7 mod 3 is -1.
  • contains and starts are case-insensitive string tests (diacritic-insensitive too, per the documented string comparison rules).
  • and/or evaluate both operands (no short-circuit) in classic Lingo. Do not port if voidP(x) or x.count = 0 literally to a short-circuit language without noting that Lingo evaluated (and errored on) the second term.
  • & coerces numbers to strings: "score: " & 10 is "score: 10".
  • String comparison with =, < etc. is case-insensitive.

Conditionals

lingo
if x > 5 then
  put "big"
else if x = 5 then
  put "exact"
else
  put "small"
end if

-- single-line form (no end if)
if x > 5 then put "big"

-- single-line with else
if x > 5 then put "big"
else put "small"

case compares one expression against candidate values (with optional multi-value lines and otherwise):

lingo
case the key of
  "a", "A": doLeft
  "d", "D": doRight
  RETURN:
    submitForm
    beep
  otherwise: nothing
end case

Loops

lingo
repeat with i = 1 to 10          -- inclusive both ends, 1-based convention
  put i
end repeat

repeat with i = 10 down to 1     -- descending
  put i
end repeat

repeat with x in someList        -- iterate list VALUES
  put x
end repeat

repeat while soundBusy(1)        -- condition loop
  nothing
end repeat

Flow control inside loops:

  • exit repeat breaks the innermost loop.
  • next repeat continues to the next iteration.
  • exit leaves the whole handler.

Blocking loops block everything. Lingo is single-threaded and cooperative. A repeat while loop freezes rendering, input, network callbacks, and sound cueing until it exits. Old code uses tight repeat loops only for sub-second waits; anything longer is done by looping the playback head on a frame (go to the frame) and testing a condition each frame.

Calling conventions

lingo
result = myFunction(a, b)   -- parentheses required when using the return value
myCommand a, b              -- command form, no return value used
sprite(3).myHandler(1)      -- dot-syntax call on an object/behavior
call(#myHandler, obj, 1)    -- send to a specific script instance
do "x = x + 1"              -- compile and execute a string at runtime

the result holds the return value of the most recent handler call made without capturing it, a frequent idiom in Director 4-era code:

lingo
doCalc 5
answer = the result