Chapter 4. Lingo Internals

This chapter covers Lingo’s internal operation, with an emphasis on comparing it to C and C++. You should be familiar with Chapter 1, before proceeding. You should also understand Director’s paradigm as explained in Chapter 1, How Director Thinks, in Director in a Nutshell. Refer to Chapter 10, Using Xtras, in Director in a Nutshell and Chapter 13, in this book for details on extending Director (especially if you are a skilled C programmer). If you are unfamiliar with C, portions of this chapter will make no sense to you. Either ignore it, or read the books on C cited at the end of Chapter 13.

Lingo Internals

There wasn’t room in this book for a full comparison of Lingo and C. Luckily, an extensive comparison can be found in the downloadable Chapter 20, Lingo for C Programmers, at http://www.zeusprod.com/nutshell/chapters/lingovsc.html. Even if you are not a C programmer, comparing Lingo to another language illuminates numerous issues, and it is highly recommended reading.

Memory, Pointers, and Memory Access

Director’s memory usage is dominated by multimedia content, primarily graphics, rich text, and nonstreaming audio. In Lingo, you don’t explicitly allocate RAM, as with malloc or alloc in C. There is no low-level access to RAM or to Director’s off-screen buffer, except via Xtras. Nor are there address or indirection operators, as in C, that would give access to a variable’s memory space. In Lingo, items are disposed of by setting them equal to 0, except for XObject instances, which are freed with mDispose. When an item in memory is no longer referenced by any variables, Director will deallocate and recapture the memory (using reference count-based garbage collection), but on an ambiguous schedule. (Refer to Chapter 9, Memory and Performance, in Director in a Nutshell for details on allocating and freeing memory, loading and unloading assets, and Director’s memory requirements.)

Compilation, Interpretation, and Performance

Lingo is not compiled, but rather tokenized into byte-code. The byte-code is the same on both the Macintosh and Windows platforms, and it is translated at runtime by the Projector into machine-specific instructions. This is convenient for cross-platform development, but it does not offer the speed of assembler. Because Lingo is not compiled, there are no separate compiler or compiler directives.

Director’s ModifyMoviePropertiesAllow Outdated Lingo option controls how Director interprets the Lingo of movies created in previous versions of Director. It is available only when upgrading movies from prior versions. Your concern is not necessarily the new Lingo features, but rather the changes in the behavior of existing functions that might break legacy code. Such a change from version to version would be exceedingly rare in a language such as C, but it is common in Director. In Director 6, the major alteration from prior versions involves sprite message passing (see Chapter 2, Events, Messages, and Scripts). Refer to Appendix B, Changes in D6 Through D6.5, and to Table 17-2 for details on other changes in D6.

Warning

Lingo executes egregiously slowly compared to C. You must be much more conscious of both media requirements and program execution times, even for simple things like loops. There is no compiler optimization. You must manually optimize your code, such as by moving assignment statements outside of repeat loops.

The performance of your Lingo code may be dwarfed by the time required to load media assets or download Shockwave data.

Dynamic Compilation

Throughout this book I use the term compile as it’s used by Macromedia to indicate the tokenizing of a script. When you recompile a script, Director checks only the syntax and for uninitialized local variables. It does not perform any type checking, nor does it validate the number of parameters to a function call. There is no Lingo equivalent to C’s lint utility, which warns about global variables that are also declared as locals, unused local variables, and similar potential errors.

Director automatically compiles any uncompiled scripts when you play your movie. There is no lengthy recompilation of scripts that haven’t changed.

Tip

There is no linking of object modules and libraries into an executable, so Director will not warn you if you refer to a nonexistent function.

An error will not occur until runtime if a function cannot be found. In Director, linking means to attach an external cast library to a Director movie; it has no relation to linking object modules into an executable, as in C. (To create an executable, see FileCreate Projector and see Chapter 8, Projectors and the Run-time Enviroment, in Director in a Nutshell.)

Lingo gives you the convenience of, but is somewhat faster than, an interpreted language. The same Lingo can be run on multiple platforms, because it is not stored in a machine-specific format such as assembler. Unfortunately, you pay for the development benefits with the slow runtime performance.

See "Compiling Scripts" in Chapter 2 for details on compiling scripts and on addressing problems with corrupted scripts.

Lingo’s dynamic compilation allows you to rapidly change and test your Lingo. Lingo can even be modified or created at runtime, by setting the scriptText of member property or using the do or value command. Even the base class of an object can be changed dynamically. (See Chapter 12, Behaviors and Parent Scripts.)

Director does not support header files, macro definitions, or inline macros, as C does. Subroutine calls take several orders of magnitude longer in Lingo than in C, so you should use in-line code when performance is crucial.

Language Definition

C’s command set is exceedingly simple, and its syntax and structure are uniform, efficient, and elegant. By comparison, Lingo is verbose, lumbering, and wildly inconsistent. Whereas C provides very low-level access, Lingo provides high-level commands to play sounds or download bitmaps. Whereas C’s command set is limited to a few dozen keywords, Lingo has more than 500 commands and properties specific to media, GUIs, and event processing. Lingo incorporates many of the support functions ordinarily found in C libraries such as stdio.

Lingo handlers are declared using the on keyword, but there is no parameter type checking or return value type checking associated with a function declaration or separate function prototype, as in C. Lingo uses the keyword the to identify properties, such as the clickOn, and the mouseH.

Lingo does not support bit-wise operations or complex numbers. They require an Xtra or other custom code.

Parameters and Variables

In Lingo, most data types, including strings, are passed by value, except lists (and other objects), which are passed by reference. Therefore, changing the contents of a list within a handler will change the list in the calling routine. See Chapter 6, Lists. Lingo doesn’t support indirection, so non-list data types can be passed back to the calling routine using return or a global declaration only. In Lingo, you can also pass multiple parameters in a list or object, which can then be modified.

In Lingo, the parentheses surrounding the arguments to a function are mandatory only when that function returns a value. Unlike C, the parentheses are optional when the calling routine is not receiving a return value.

In Lingo, if the calling routine omits an argument to a function, the corresponding parameter inside the function is set to VOID. In C, an error would be generated by the precompiler, or the omitted argument would result in the corresponding parameter holding a meaningless value.

Lingo, as with C, allows nested function calls, such as:

set x = random (max(y, z))

Unlike C, in Lingo, you can not perform a variable assignment within a nested function call. (Recall that Lingo assignment requires the keyword set). These attempts would cause syntax errors in Lingo:

if (x = max(y, z) > 7) then....
if (set x = max(y, z) > 7) then....

Refer also to the "Parameters and Arguments" section of Chapter 1. Lingo variable and function names are case-insensitive and can be exceedingly long (about 250 characters). In C, variable and function names are strictly case-sensitive.

Declarations, Type Checking, and Conversion

In comparison to C, Lingo’s data typing is loose to the point of being obscene. Lingo never explicitly declares a variable’s data type, nor a function’s return type, as opposed to C, which is strongly typed. A Lingo variable’s data type is implicitly defined by the type of data assigned to it, and it can be changed by simply assigning a new datum to it.

Data Type Checking and Conversion

Lingo does not verify the number or type of arguments to a function call, nor does it support function prototyping, as in C. In C, because every variable’s type is fixed, it is assumed that you know a datum’s type. Because Lingo is so loosely typed, it provides functions—most notably ilk(), integerP(), floatP(), stringP(), voidP(), and objectP()—to determine a datum’s type. Refer to Table 5-4" in Chapter 5, Data Types and Expressions. Refer to the "Parameters and Arguments" section of Chapter 1 for techniques for manually verifying parameters passed to a function.

Lingo casually converts between various data types with sometimes puzzling results, as opposed to C-style coercion and conversion, which is usually explicit. Refer to “Type Conversion” in Chapter 5.

Lists, Arrays, and Structures

Lingo lists encompass both C arrays and structures. Unlike C arrays, Lingo lists are allocated automatically and can change size arbitrarily. There is no dimension statement or malloc statement in Lingo, and a single list can contain data of differing types simultaneously. A two-dimensional array is implemented as a list of lists in Lingo, but its elements cannot be referenced as a one-dimensional array, as is often done in C. Unlike most Lingo data types, lists are passed by reference, not value. See Chapter 6, Lists, for more details. See the downloadable Chapter 20 for a complete comparison of data types in Lingo and C.

Strings

String are a distinct data type in Lingo, not an array of characters as in C. When working with Lingo strings, you need not allocate a specific number of bytes, nor should you worry about null-termination. A Lingo string with no characters has zero-length, as reported by the length() function. Lingo strings are allocated automatically and dynamically. Whereas a Lingo field is limited to 32,000 characters (31.25 K), a string variable can be much larger (up to 2 MB). Unfortunately, because Lingo strings are passed on the stack by value (which is very inefficient), attempting to call the length() function with such a large string can overflow Director’s stack.

Warning

Passing strings on the stack by value is one reason Lingo’s text processing is egregiously slow.

You cannot directly modify a string argument to a Lingo function. Instead, you must modify a copy of the string and pass it back as a return value. Refer to Chapter 7, Strings, for many more details on string manipulation in Lingo, especially concatenation. See Chapter 14, External Files, for details on reading and writing strings using external files. See Chapter 12, Text and Fields, in Director in a Nutshell for details on displaying strings on stage.

Event and Error Trapping

Director implements a so-called event loop in which it continually waits for events. In Lingo, you need only create handlers, such as on mouseUp, that respond to these events, such as mouseUp (see Chapter 2). There is no need to subscribe to any particular events, as events are dispatched to the appropriate scripts automatically.

Lingo does not have robust error trapping or true interrupts. Errors typically generate an alert dialog box. You can perform your own error checking to prevent this. See "The AlertHook“” section in Chapter 3, Lingo Coding and Debugging Tips.

Debugging

Director has a Debugger (see Chapter 3), but it is not as robust as traditional debugging tools. For one thing, you can change the code at runtime, in which case the Debugger will warn you that scripts need to be recompiled! If you close the Debugger window, Director immediately regains control. You must stop the Director movie itself to halt Lingo execution and recompile your scripts.

In C programming, the application programmer is at the top of the hierarchy. If you set a breakpoint in the C debugger, you can step through your program indefinitely. Even if you call library routines to perform lower-level functions, control always boomerangs back to you.

By contrast, in Director the Lingo programmer is sandwiched between the library of Lingo commands (on the bottom) and Director’s main event loop (on the top). (And the Debugger can debug only Lingo code. It has no access to the C code of Xtras or of Director itself, nor direct access to the Lingo of MIAWs.) Because Director’s event loop is above your Lingo code, it periodically regains control, and there is nothing you can do to stop it. Suppose you set a breakpoint in an on mouseDown handler. At the end of that handler, even if you single-step through your Lingo code, control returns to Director’s event processing loop. The only way to regain control is to set another breakpoint in another Lingo handler that you expect to be reached at a later time. Thus, there is no way to follow a given event as it passes through the Lingo messaging hierarchy.

Likewise, there is no way to cause an immediate break into whatever Lingo code may be executing at the moment. Director will break into Lingo only where you have previously set a break point. If you understand the material covered in detail in Chapter 1 and Chapter 2, you will be able to figure out where to set your breakpoints.

The Director movie stops when you press Cmd-. (Mac) or Ctrl-. (Windows) to interrupt execution, but you aren’t afforded the opportunity to enter the Debugger. In fact, there is a good chance that Director’s C code, and not the Lingo interpreter, is running at any given time, so there is no way that Director can pass control to the Lingo Debugger at the arbitrary time at which the user halts Director’s execution.

File I/O, Character Streams, and Printing

All Lingo file input and output is done using the FileIO Xtra, which comes with Director. Refer to Chapter 14.

Lingo does not support pipes or redirection. Editable fields automatically accept keystrokes, but you can use an on keyDown handler to trap keyboard input explicitly. Refer to Chapter 10, Keyboard Events.

Instead of C’s formatted printf statement, use the put command to print items in the Message window, which behaves as stdout. You can redirect the output from the Message window to a log file using the traceLogFile property.

Lingo Object-Oriented Programming Versus C++

Lingo provides an object-oriented model, described in Chapter 12. Refer also to Chapter 12, Parent Scripts and Child Objects, of Macromedia’s Learning Director manual. Director allows you to mix and match OOP and procedural programming. Table 4-1 compares the terminology for Lingo and C++.

Table 4-1. Lingo versus C++ Terminology

C++ Term

Lingo Term

Class

Parent script

Base class or super class

Ancestor script

Instance

Child object or child instance

Inherited or derived class (descendant)

A child object with an ancestor

Handler or procedure

Method

Instance variable or member variable

Property variable

Constructor

new method

Deconstructor

None; set variable referencing object to 0

this

me

Class Hierarchy and Inheritance

Lingo’s class hierarchies are much looser than C++’s. In C++, a class’s base class is defined in the constructor, before the class is even compiled. In Lingo, a child object’s ancestor property is set at runtime; it can even change dynamically. A single child object can inherit only from a single ancestor script (multiple inheritance is not supported). For a child object to inherit additional methods from additional ancestors, its direct ancestor must declare its own ancestor. Note that in Lingo, the term parent refers not to a parent class or base class, but merely to the parent script (that is, class definition) of which the child object is an instance. In the simplest case, a single parent script is instantiated to create one or more copies of itself (so-called child objects). Thus, a child object is not necessarily a descendent of some base class, and its parent script is not necessarily an inherited or derived class.

The following features of C++ have no equivalent in Lingo:

  • Abstract class

  • Multiple inheritance

  • Templates

Encapsulation

It is impossible to encapsulate a child object fully. Lingo objects can be manipulated by some list functions, and they are passed by reference, as are Lingo lists. The Lingo count() and getAt() functions can extract a child object’s property variables. These properties can also be read or set by code external to the object using:

set the property of childObject = value

There is no equivalent to C++’s static, public, private, and friend handlers. Methods within a child object are private by default, but any message can be sent to a child object using the call() function. Its ancestor can be called using callAncestor(). A child can refer to its ancestor’s properties (after declaring its ancestor) using:

set the property of the ancestor of me = value

Refer to Chapter 12 for a thorough discussion of objects in Director.

The downloadable Chapter 20 (cited earlier) covers many more details that dead-tree publishing constraints did not allow here. It is an invaluable Lingo syntax primer for C programmers, and a glimpse into the world of C for Linguists. It is an in-depth comparison of Lingo and C, including numerous useful tables comparing data types and commands in the two languages.

Get Lingo in a Nutshell now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.