This appendix outlines the syntax for GemStone Smalltalk and introduces the important kinds of GemStone Smalltalk objects.
GemStone and ANSI Smalltalk
The relationship between GemStone Smalltalk and the ANSI Smalltalk standard.
GemStone Smalltalk
An overview of the Smalltalk Syntax.
Blocks
How blocks are used in GemStone Smalltalk.
GemStone Smalltalk BNF
GemStone Smalltalk BNF
GemStone’s programming language, GemStone Smalltalk, is a dialect of the Smalltalk programming language. The Smalltalk language standard is defined by an ANSI Smalltalk standard. While GemStone follows this standard, there are places where either for historical reasons or by choice, GemStone Smalltalk does not follow the ANSI standard.
Some known places in which GemStone Smalltalk does not conform to the ANSI standard:
The limits in GemStone, such as the maximum length of an identifier, are generally higher than the ANSI minimum requirement. While this makes working in GemStone more flexible, adhering to the ANSI limits, or the limits for other Smalltalk dialects, makes it easier to port GemStone code to other platforms.
Number of named instance variables per object (including inherited) |
||
Every object is an instance of a class, taking its methods and its form of data storage from its class. Defining a class thus creates a kind of template for a whole family of objects that share the same structure and methods. Instances of a class are alike in form and in behavioral repertoire, but independent of one another in the values of the data they contain.
Classes are much like the data types (string, integer, etc.) provided by conventional languages; the most important difference is that classes define actions as well as storage structures. In other words, Algorithms + Data Structures = Classes.
Smalltalk provides a number of predefined classes that are specialized for storing and transforming different kinds of data. Instances of class Float, for example, store floating-point numbers, and class Float provides methods for doing floating-point arithmetic. Floats respond to messages such as +, -, and reciprocal.
Instances of class Array store sequences of objects and respond to messages that read and write array elements at specified indices.
The Smalltalk classes are organized in a treelike hierarchy, with classes providing the most general services nearer the root, and classes providing more specialized functions nearer the leaves of the tree. This organization takes advantage of the fact that a class’s structure and methods are automatically conferred on any classes defined as its subclasses. A subclass is said to inherit the properties of its parent and its parent’s ancestors.
GemStone Smalltalk is case-sensitive; that is, names such as “SuperClass,” “superclass,” and “superClass” are treated as unique items by the GemStone Smalltalk compiler.
Classes are created using a number of class creation methods, defined on the class Class. For example, following message expression makes a new subclass of class Object, the class at the top of the class hierarchy:
Object subclass: 'Animal'
instVarNames: #()
classVars: #()
classInstVars: #()
poolDictionaries: {}
inDictionary: UserGlobals
This subclass creation message establishes a name (’Animal’) for the new class and installs the new class in a Dictionary called UserGlobals. The String used for the new class’s name must follow the general rule for variable names — that is, it must begin with an alphabetic character and its length must not exceed 1024 characters. Installing the class in UserGlobals makes it available for use in the future—you need only write the name Animal in your code to refer to the new class. For more on class creation, see Chapter 2.
The basic syntactic unit of a Smalltalk program is the statement. A lone statement needs no delimiters; multiple statements are separated by periods:
a := 2.
b := 3.
In a group of statements to be executed en masse, a period after the last statement is optional.
A statement contains one or more expressions, combining them to perform some reasonable unit of work, such as an assignment or retrieval of an object.
GemStone Smalltalk usually treats a string of characters enclosed in quotation marks as a comment—a descriptive remark to be ignored during compilation. Here is an example:
"This is a comment."
A quotation mark does not begin a comment in the following cases:
A comment terminates tokens such as numbers and variable names. For example, GemStone Smalltalk would interpret the following as two numbers separated by a space (by itself, an invalid expression):
2" this comment acts as a token terminator"345
An expression is a sequence of characters that Smalltalk can interpret as a reference to an object. Some references are direct, and some are indirect.
Expressions that name objects directly include both variable names and literals such as numbers and strings. The values of those expressions are the objects they name.
An expression that refers to an object indirectly by specifying a message invocation has the value returned by the message’s receiver. You can use such an expression anywhere you might use an ordinary literal or a variable name. This expression:
2 negated
has the value (refers to) -2, the object that 2 returns in response to the message negated.
The following sections describe the syntax of GemStone Smalltalk expressions and tell you something about their behavior.
A GemStone Smalltalk expression can contain a combination of the following:
The following sections discuss each of these kinds of expression in turn.
A literal expression is a representation of some object such as a character or string whose value or structure can be written out explicitly. The kinds of GemStone Smalltalk literals are:
In Smalltalk, literal numbers look and act much like numbers in other programming languages. Like other Smalltalk objects, numbers receive and respond to messages. Most of those messages are requests for arithmetic operations. In general, Smalltalk numeric expressions do the same things as their counterparts in other programming languages. For example:
5 + 5
A literal floating point number must include at least one digit after the decimal point:
5.0
You can express very large and very small numbers compactly with scientific notation. To raise a number to some exponent, simply append the letter “e” and a numeric exponent to the number’s digits. For example:
8.0e2
represents 800.0. The number after the e represents an exponent (base 10) to which the number preceding the e is to be raised. The result is always a floating point number. Here are more examples:
1e-3 represents 0.001
1.5e0 represents 1.5
The literal numeric type GemStone/S 64 Bit supports are:
For details, see GemStone Smalltalk Lexical Tokens.
To represent a number in a nondecimal base literally, write the number’s base (in decimal), followed by the radix “r” or character "#", and then the number itself. Here, for example, is how you could write octal 23 and hexadecimal FF:
8#23
16rFF
A Smalltalk character literal represents a character, such as one of the symbols of the alphabet. To create a character literal, write a dollar sign ($) followed by the character’s alphabetic symbol. Here are some examples:
$b $B $4 $? $$
If a nonprinting ASCII character such as a tab or a form feed follows the dollar sign, Smalltalk creates the appropriate internal representation of that character.
GemStone Smalltalk interprets this statement, for example, as a representation of ASCII character 32:
$ . "Creates the character representing a space (ASCII 32)"
In this example, the period following the space acted as a statement terminator. If no space had separated the dollar sign from the period, GemStone Smalltalk would have interpreted the expression as the character literal representing a period.
Literal strings represent sequences of characters. They are instances of the class String, described in Chapter 4, “Collection and Stream Classes”.A literal string is a sequence of characters enclosed by single quotation marks. These are literal instances of String:
'Intellectual passion drives out sensuality.'
'A difference of taste in jokes is a great strain on the affections.'
When you want to include apostrophes in a literal string, double them:
'You can''t make omelettes without breaking eggs.'
GemStone Smalltalk faithfully preserves control characters when it compiles literal strings. The following example creates a String containing a line feed (ASCII 10), the end-of-line character:
'Control characters such as line feeds are significant in literal strings.'
Strings may hold characters with values up to 255, that is, characters that can be representing in a single byte. Characters themselves may have values much higher. If a string includes any characters larger than 255, it is converted to a DoubleByteString. If any of the characters require more than two bytes, it becomes a QuadByteString. For example, this is a DoubleByteString:
'Škoda'
A literal Symbol is similar to a literal String. It is a sequence of characters preceded by a pound sign (#). For example:
#stuff
#nonsense
#may_24_thisYear
Literal Symbols specified in this way must be legal identifiers or keywords: they must begin with a letter, contain only alphanumeric characters, underscore, and colon. A Symbol can contain other characters, or start with a number: in this case, they must be preceded by a pound sign (#) and must also be delimited by single quotation marks. For example:
#'Gone With the Wind'
As with strings that contain characters that require more than a byte to represent, DoubleByteSymbol and QuadByteSymbol are used for symbol literals that include characters with values over 255.
Arrays can hold objects of any type, and they respond to messages that read and write individual elements or groups of elements.
A literal Array can contain only other literals—Characters, Strings, Symbols, other literal Arrays, and three “special literals” (true, false, nil). The elements of a literal Array are enclosed in parentheses and preceded by a pound sign (#). White space must separate the elements.
Here is an Array that contains two Strings, a literal Array, and a third String:
#('string one' 'string two' #('another' 'Array') 'string3')
The following Array contains a String, a Symbol, a Character, a Number, and a Boolean:
#('string one' #symbolOne $c 4 true)
ByteArray literals are similar, but may only hold SmallIntegers in the range 0 to 255, and use square brackets instead of parenthesis.
#[99 97 116]
Besides Array literals, you may also specify Array constructors in your code, which are used similarly, but follow quite different rules. For a discussion of array constructors, Array Constructors.
A variable name is a sequence of characters of either or both cases. A variable name must begin with an alphabetic character or an underscore (“_”), but it can contain numerals. Spaces are not allowed, and the underscore is the only acceptable punctuation mark. Here are some permissible variable names:
zero
relationalOperator
Top10SolidGold
A_good_name_is_better_than_precious_ointment
Most Smalltalk programmers begin local variable names with lowercase letters and global variable names with uppercase letters. When a variable name contains several words, Smalltalk programmers usually begin each word with an uppercase letter (sometimes called “camelcase”). You are free to ignore either of these conventions, but remember that Smalltalk is case-sensitive. The following are all different names to Smalltalk:
VariableName
variableName
variablename
Variable names can contain up to 1024 characters.
GemStone Smalltalk requires you to declare new variable names (implicitly or explicitly) before using them. The simplest kind of variable to declare is the temporary variable. Temporary variables are so called because they are defined only for one execution of the set of statements in which they are declared.
To declare a temporary variable, you surround it with vertical bars as in this example:
| myTempVariable |
myTempVariable := 2.
You can declare at most 1013 temporary variables for a set of statements. Once declared, a variable can name objects of any kind.
To store a variable for later use, or to make its scope global, you must put it in one of GemStone’s shared dictionaries that GemStone Smalltalk uses for symbol resolution. For example:
| myTempVariable |
myTempVariable := 2.
UserGlobals at: #MyPermanentVariable put: myTempVariable.
Subsequent references to MyPermanentVariable return the value 2.
You can change the objects to which most variable names refer simply by assigning them new objects. However, five GemStone Smalltalk variables have values that cannot be changed by assignment; they are therefore called pseudovariables. They are:
Refers to an object representing a null value. Variables not assigned another value automatically refer to nil. nil is an instance of UndefinedObject.
Refers to the object representing logical truth. true is an instance of Boolean.
Refers to the object representing logical false. false is an instance of Boolean.
Refers to the receiver of the message, which differs according to the context. self may be used anywhere a method argument or method temporary would be used, except self is not allowed on the left side of an assignment. When self is used in code that is not part of a method, it resolves to nil.
Refers to the receiver of the message, but the search for the method to execute will start in the superclass of the class in which the sending method was compiled. super may only be used as the receiver of a message send, in code within a method.
Assignment statements in Smalltalk look like assignment statements in many other languages. The following statement assigns the value 2 to the variable MightySmallInteger:
MightySmallInteger := 2.
The next statement assigns the same String to two different variables (C programmers may notice the similarity to C assignment syntax):
nonmodularity := interdependence := 'No man is an island'.
Smalltalk objects communicate with one another by means of messages. Most of your effort in Smalltalk programming will be spent in writing expressions in which messages are passed between objects. This subsection discusses the syntax of those message expressions.
You have already seen several examples of message expressions:
2 + 2
5 + 5
In fact, the only GemStone Smalltalk code segments you have seen that are not message expressions are literals, variables, and simple assignments:
2 "a literal"
variableName "a variable"
MightySmallInteger := 2. "an assignment"
The ubiquity of message-passing is one of the hallmarks of object-oriented programming.
A message expression consists of:
GemStone represents selectors internally as symbols, and almost all symbols that confirm to the unary, binary, or keyword selector patterns are acceptable as a selectors. For details on legal selectors, see the BNF here.
There are a few selectors that have been reserved for the sole use of the GemStone kernel classes. The compiler will not allow you to compile methods with reserved selectors. To see the list of reserved and optimized selectors, execute:
GsNMethod optimizedSelectors
In addition, the following methods are optimized or inlined in the class SmallInteger:
+ - * = ~= < <= > >=
The selectors isNil, notNil, and yourself are optimized by default, so methods with these selectors cannot be compiled.
This optimization is configurable, and can be removed. To see the list of conditionally optimized selectors, execute GsNMethod configurableOptimizedSelectors.
To remove the optimization, as SystemUser you must reset GsNMethods’s class variable OptimizedSelectors to the desired list; note that symbols other than those in the defined optimized selectors will be ignored. This can be done using an expression such as GsNMethod _classVars at: #OptimizedSelectors put: anArray.
Then you must run upgradeImage or otherwise recompile GemStone source code, and recompile all application methods, so existing compiled methods are updated.
In the following message expression, the object 2 is the receiver, + is the selector, and 8 is the argument:
2 + 8
When 2 sees the selector +, it looks up the selector in its private memory and finds instructions to add the argument (8) to itself and to return the result. In other words, the selector + tells the receiver 2 what to do with the argument 8. The object 2 returns another numeric object 10, which can be stored with an assignment:
myDecimal := 2 + 8.
The selectors that an object understands (that is, the selectors for which instructions are stored in an object’s instruction memory or “method dictionary”) are determined by the object’s class.
The simplest kind of message consists only of a single identifier called a unary selector. The selector negated, which tells a number to return its negative, is representative:
7 negated
-7
Here are some other unary message expressions:
9 reciprocal. "returns the reciprocal of 9"
myArray last. "returns the last element of Array myArray"
DateTime now. "returns the current date and time"
Binary message expressions contain a receiver, a single selector consisting of one or two nonalphanumeric characters, and a single argument. You are already familiar with binary message expressions that perform addition. Here are some other binary message expressions (for now, ignore the details and just notice the form):
8 * 8 "returns 64"
4 < 5 "returns true"
myObject = yourObject "returns true if myObject and
yourObject have the same value"
Keyword messages are the most common. Each contains a receiver and up to 15 keyword and argument pairs. In keyword messages, each keyword is a simple identifier ending in a colon.
In the following example, 7 is the receiver, rem: is the keyword selector, and 3 is the argument:
7 rem: 3 "returns the remainder from the division of 7 by 3"
Here is a keyword message expression with two keyword-argument pairs:
| arrayOfStrings |
arrayOfStrings := Array new: 4.
arrayOfStrings at: (2 + 1) put: 'Curly'.
"puts 'Curly' at index position 3 in the receiver"
In a keyword message, the order of the keyword-argument pairs (at:arg1 put:arg2) is significant.
In a previous example, one message expression was nested within another, and parentheses set off the inner expression to make the order of evaluation clear. It happens that the parentheses were optional in that example. However, in GemStone Smalltalk as in most other languages, you sometimes need parentheses to force the compiler to interpret complex expressions in the order you prefer.
Combinations of unary messages are quite simple; GemStone Smalltalk always groups them from left to right and evaluates them in that order. For example:
9 reciprocal negated
is evaluated as if it were parenthesized like this:
(9 reciprocal) negated
That is, the numeric object returned by 9 reciprocal is sent the message negated.
Binary messages are also invariably grouped from left to right. For example, GemStone Smalltalk evaluates:
2 + 3 * 2
as if the expression were parenthesized like this:
(2 + 3) * 2
This expression returns 10. It may be read: “Take the result of sending + 3 to 2, and send that object the message * 2.”
All binary selectors have the same precedence. Only the sequence of a string of binary selectors determines their order of evaluation; the identity of the selectors doesn’t matter.
However, when you combine unary messages with binary messages, the unary messages take precedence. Consider the following expression, which contains the binary selector + and the unary selector negated:
2 + 2 negated
0
This expression returns the result 0 because the expression 2 negated executes before the binary message expression 2 + 2. To get the result you may have expected here, you would need to parenthesize the binary expression like this:
(2 + 2) negated
-4
Finally, binary messages take precedence over keyword messages. For example:
myArrayOfNums at: 2 * 2
would be interpreted as a reference to myArrayofNums at position 4. To multiply the number at the second position in myArrayOfNums by 2, you would need to use parentheses like this:
(myArrayOfNums at: 2) * 2
1. Parenthetical expressions are always evaluated first.
2. Unary expressions group left to right, and they are evaluated before binary and keyword expressions.
3. Binary expressions group from left to right, as well, and take precedence over keyword expressions.
4. GemStone Smalltalk executes assignments after message expressions.
You will often want to send a series of messages to the same object. By cascading the messages, you can avoid having to repeat the name of the receiver for each message. A cascaded message expression consists of the name of the receiver, a message, a semicolon, and any number of subsequent messages separated by semicolons.
| arrayOfPoets |
arrayOfPoets := Array new.
(arrayOfPoets add: 'cummings'; add: 'Byron'; add: 'Rimbaud';
yourself)
is a cascaded message expression that is equivalent to this series of statements:
| arrayOfPoets |
arrayOfPoets := Array new.
arrayOfPoets add: 'cummings'.
arrayOfPoets add: 'Byron'.
arrayOfPoets add: 'Rimbaud'.
arrayOfPoets
You can cascade any sequence of messages to an object. And, as always, you are free to replace the receiver’s name with an expression whose value is the receiver.
Most of the syntax described in this chapter so far is standard Smalltalk syntax. However, GemStone Smalltalk also includes a syntactic construct called a Array constructor. An Array constructor is similar to a literal array, but its elements can be written as nonliteral expressions as well as literals. GemStone Smalltalk evaluates the expressions in an Array constructor at run time.
Array constructors look a lot like literal Arrays; the differences are that array constructors are enclosed in braces and have their elements delimited by periods.
The following example shows an Array constructor whose last element, represented by a message expression, has the value 4.
"An Array constructor"
{'string one' . #SymbolOne . $c . 2+2}
NOTE
The Array constructor is not part of the Smalltalk standard. You should avoid its use in any code that might be ported to an other Smalltalk dialect. Instead, use a message send constructor such as Array class >> #with:, such as Array with: 'string one' with: $c with: 2+2.
Because any valid GemStone Smalltalk expression is acceptable as an array constructor element, you are free to use variable names as well as literals and message expressions:
| aString aSymbol aCharacter aNumber |
aString := 'string one'.
aSymbol := #symbolOne.
aCharacter := $c.
aNumber := 4.
{aString . aSymbol . aCharacter . aNumber}
The differences in the behavior of array constructors versus literal arrays can be subtle. For example, the literal array:
#(123 huh 456)
is interpreted as an array of three elements: a SmallInteger, aSymbol, and another SmallInteger. This is true even if you declare the value of huh to be a SmallInteger such as 88, as shown in this example:
| huh |
huh := 88.
#( 123 huh 456 )
[20176897 sz:3 cls: 66817 Array] an Array
#1 [986 sz:0 cls: 74241 SmallInteger] 123 == 0x7b
#2 [27086593 sz:3 cls: 110849 Symbol] huh
#3 [3650 sz:0 cls: 74241 SmallInteger] 456 == 0x1c8
The same declaration used in an array constructor, however, produces an array of three SmallIntegers:
| huh |
huh := 88.
{ 123 . huh . 456 }
[20192001 sz:3 cls: 66817 Array] an Array
#1 [986 sz:0 cls: 74241 SmallInteger] 123 == 0x7b
#2 [706 sz:0 cls: 74241 SmallInteger] 88 == 0x58
#3 [3650 sz:0 cls: 74241 SmallInteger] 456 == 0x1c8
With the exception of Array constructors, most of the syntax described in this chapter so far is standard Smalltalk syntax. GemStone Smalltalk also includes a syntactic construct called a path. A path is a special kind of expression that returns the value of an instance variable.
A path is an expression that contains the names of one or more instance variables separated by periods; a path returns the value of the last instance variable in the series. The sequence of the names reflects the order of the objects’ nesting; the outermost object appears first in a path, and the innermost object appears last. The following path points to the instance variable name, which is contained in the object anEmployee:
anEmployee.name
The path in this example returns the value of instance variable name within anEmployee.
If the instance variable name contained another instance variable called last, the following expression would return the value of last:
anEmployee.name.last
NOTE
Use paths only for their intended purposes. Although you can use a path anywhere an expression is acceptable in a GemStone Smalltalk program, paths are intended for specifying indexes, formulating queries, and sorting. In other contexts, a path returns its value less efficiently than an equivalent message expression. Paths also violate the encapsulation that is one of the strengths of the object-oriented data model. Using them can circumvent the designer’s intention. Finally, paths are not standard Smalltalk syntax. Therefore, programs using them are less portable than other GemStone Smalltalk programs.
Previous discussions have spoken of the “value of an expression” or the “object returned by an expression.” Whenever a message is sent, the receiver of the message returns an object. You can think of this object as the message expression’s value, just as you think of the value computed by a mathematical function as the function’s value.
You can use an assignment statement to capture a returned object:
| myVariable |
myVariable := 8 + 9. "assign 17 to myVariable"
myVariable "return the value of myVariable"
17
You can also use the returned object immediately in a surrounding expression:
"puts 'Moe' at position 2 in arrayOfStrings"
| arrayOfStrings |
arrayOfStrings := Array new: 4.
(arrayOfStrings at: 1+1 put: 'Moe'; yourself) at: 2
And if the message simply adds to a data structure or performs some other operation where no feedback is necessary, you may simply ignore the returned value.
A GemStone Smalltalk block is an object that contains a sequence of instructions. The sequence of instructions encapsulated by a block can be stored for later use, and executed by simply sending the block the unary message value. Blocks find wide use in GemStone Smalltalk, especially in building control structures.
A literal block is delimited by brackets and contains one or more GemStone Smalltalk expressions separated by periods. Here is a simple block:
[3.2 rounded]
To execute this block, send it the message value.
[3.2 rounded] value
3
When a block receives the message value, it executes the instructions it contains and returns the value of the last expression in the sequence. The block in the following example performs all of the indicated computations and returns 8, the value of the last expression.
[89*5. 3+4. 48/6] value
8
You can store a block in a simple variable:
| myBlock |
myBlock := [3.2 rounded].
myBlock value.
3
or store several blocks in more complex data structures, such as Arrays:
| factorialArray |
factorialArray := Array new.
factorialArray at: 1 put: [1];
at: 2 put: [2 * 1];
at: 3 put: [3 * 2 * 1];
at: 4 put: [4 * 3 * 2 * 1].
(factorialArray at: 3) value
6
Because a block’s value is an ordinary object, you can send messages to the value returned by a block.
| myBlock |
myBlock := [4 * 8].
myBlock value / 8
4
The value of an empty block is nil.
[ ] value
nil
Blocks are especially important in building control structures. The following section discusses using blocks in conditional execution.
You can build blocks that take arguments. To do so, precede each argument name with a colon, insert it at the beginning of the block, and append a vertical bar to separate the arguments from the rest of the block.
Here is a block that takes an argument named myArg:
[ :myArg | 10 + myArg]
To execute a block that takes an argument, send it value: anArgument. For example:
| myBlock |
myBlock := [ :myArg | 10 + myArg].
myBlock value: 10.
20
The following example creates and executes a block that takes two arguments. Notice the use of the two-keyword message value:value:.
| divider |
divider := [:arg1 :arg2 | arg1 / arg2].
divider value: 4 value: 2
2
A block assigns actual parameter values to block variables in the order implied by their positions. In this example, arg1 takes the value 4 and arg2 takes the value 2.
Variables used as block arguments are known only within their blocks; that is, a block variable is local to its block. A block variable’s value is managed independently of the values of any similarly named instance variables, and GemStone Smalltalk discards it after the block finishes execution. This example illustrates this:
| aVariable |
aVariable := 1.
[:aVariable | aVariable ] value: 10.
aVariable
1
You cannot assign to a block variable within its block. This code, for example, would elicit a compiler error:
"The following expression attempts an invalid assignment
to a block variable."
[:blockVar | blockVar := blockVar * 2] value: 10
Most computer languages, GemStone Smalltalk included, execute program instructions sequentially unless you include special flow-of-control statements. These statements specify that some instructions are to be executed out of order; they enable you to skip some instructions or to repeat a block of instructions. Flow of control statements are usually conditional; they execute the target instructions if, until, or while some condition is met.
GemStone Smalltalk flow of control statements rely on blocks because blocks so conveniently encapsulate sequences of instructions. GemStone Smalltalk’s most important flow of control structures are message expressions that execute a block if or while some object or expression is true or false. GemStone Smalltalk also provides a control structure that executes a block a specified number of times.
You will often want GemStone Smalltalk to execute a block of code only if some condition is true or only if it is false. GemStone Smalltalk provides the messages ifTrue: aBlock and ifFalse: aBlock for that purpose. This example contains both of these messages:
5 = 5 ifTrue: ['yes, five is equal to five'].
yes, five is equal to five
5 > 10 ifFalse: ['no, five is not greater than ten'].
no, five is not greater than ten
In the first of these examples, GemStone Smalltalk initially evaluates the expression (5 = 5). That expression returns the value true (a Boolean), to which GemStone Smalltalk then sends the selector ifTrue:. The receiver (true) looks at itself to verify that it is, indeed, the object true. Because it is, it proceeds to execute the block passed as an argument to ifTrue:, and the result is a String.
The receiver of ifTrue: or ifFalse: must be Boolean; that is, it must be either true or false. In the above example, the expressions (5 = 5) and (5 > 10) returned true and false, respectively, because GemStone Smalltalk numbers know how to compute and return those values when they receive messages such as = and >.
You will often want to direct your program to take one course of action if a condition is met and a different course if it isn’t. You could arrange this by sending ifTrue: and then ifFalse: in sequence to a Boolean (true or false) expression. For example:
2 < 5 ifTrue: ['two is less than five'].
two is less than five
2 < 5 ifFalse: ['two is not less than five'].
nil
However, GemStone Smalltalk lets you express the same instructions more compactly by sending the single message ifTrue: block1 ifFalse: block2 to an expression or object that has a Boolean value. Which of that message’s arguments GemStone Smalltalk executes depends upon whether the receiver is true or false. In this example, the receiver is true:
2 < 5 ifTrue: ['two is less than five']
ifFalse: ['two is not less than five'].
two is less than five
You will also sometimes want to execute a block of instructions repeatedly as long as some condition is true, or as long as it is false. The messages whileTrue: aBlock and whileFalse: aBlock give you that ability. Any block that has a Boolean value responds to these messages by executing aBlock repeatedly while it (the receiver) is true (whileTrue:) or false (whileFalse:).
Here is an example that repeatedly adds 1 to a variable until the variable equals 5:
| sum |
sum := 0.
[sum = 5] whileFalse: [sum := sum + 1].
sum
5
The next example calculates the total payroll of a miserly but egalitarian company that pays each employee the same salary.
| totalPayroll numEmployees salariesAdded standardSalary |
totalPayroll := 0.00.
salariesAdded := 0.
numEmployees := 40.
standardSalary := 5000.00.
[salariesAdded < numEmployees] whileTrue:
[totalPayroll := totalPayroll + standardSalary.
salariesAdded := salariesAdded + 1].
totalPayroll
200000.0
Blocks also accept two unary conditional repetition messages, untilTrue and untilFalse. These messages cause a block to execute repeatedly until the block’s last statement returns either true (untilTrue) or false (untilFalse).
The following example uses untilTrue (rather than whileFalse:).
| sum |
sum := 0.
[sum := sum + 1. sum = 5] untilTrue.
sum
%
5
When GemStone Smalltalk executes the block initially (by sending it the message value), the block’s first statement adds one to the variable sum. The block’s second statement asks whether sum is equal to 5; since it isn’t, that statement returns false, and GemStone Smalltalk executes the block again. GemStone Smalltalk continues to reevaluate the block as long as the last statement returns false (that is, while sum is not equal to 5).
The descriptions of classes Boolean and Block in the image describe these flow of control messages and others.
GemStone Smalltalk is a free-format language. A space, tab, line feed, form feed, or carriage return affects the meaning of a GemStone Smalltalk expression only when it separates two characters that, if adjacent to one another, would form part of a meaningful token.
In general, you are free to use whatever spacing makes your programs most readable. The following are all equivalent:
{'string one'.2+2.'string three'.$c.9*arglebargle}
{ 'string one' . 2+2 . 'string three' . $c . 9*arglebargle }
{ 'string one'.
2 + 2.
'string three'.
$c.
9 * arglebargle }
This section provides a complete BNF description of GemStone Smalltalk. Here are a few notes about interpreting the grammar:
This defines the syntactic production ‘A’ in terms of the expression on the right side of the equals sign.
The vertical bar ‘|’ defines alternatives. In this case, the production “B” is one of either “C” or “D”.
A symbol in accents is a literal symbol.
A sequence of two or more productions means the productions in the order of their appearance.
Brackets indicate zero or one optional productions.
Braces indicate zero or more occurrences of the productions contained within.
Parentheses can be used to remove ambiguity.
In the GemStone Smalltalk syntactic productions in Figure A.1, white space is allowed between tokens. White space is required before and after the ‘_’ character.
AExpression = Primary [ AMessage { ';' ACascadeMessage } ]
ABinaryMessage = [ EnvSpecifier ] BinarySelector Primary [ UnaryMessages ]
ABinaryMessages = ABinaryMessage { ABinaryMessage }
ACascadeMessage = UnaryMessage | ABinaryMessage | AKeyWordMessage
AKeyWordMessage = [ EnvSpecifier | RubyEnvSpecifier ] AKeyWordPart { AKeyWordPart }
AKeyWordPart = KeyWord Primary UnaryMessages { ABinaryMessage }
AMessage = [ UnaryMessages ] [ ABinaryMessages ] [ AKeyWordMessage ]
AnyTerm = Operand [ Operator Operand ]
Array = '(' { ArrayItem } ')'
ArrayBuilder = '#[' [ AExpression { ',' AExpression } ] ']'
(exists only if System configurationAt:#GemConvertArrayBuilder is true)
ArrayItem = Number | SymbolArrayItem | SymbolLiteral | StringLiteral |
CharacterLiteral | Array | ArrayLiteral | ByteArrayLiteral
ArrayLiteral = '#' Array
Assignment = VariableName ':=' Statement | VariableName ' _ ' Statement
BinaryMessage = [ EnvSpecifier | RubyEnvSpecifier ] BinarySelector Primary
[ UnaryMessages ]
BinaryMessages = BinaryMessage { BinaryMessage }
BinaryPattern = BinarySelector VariableName
Block = '[' [ BlockParameters ] [ Temporaries ] Statements ']'
BlockParameters = { Parameter } '|'
ByteArrayLiteral = '#' '[' [ Number { Number } ] ']'
(exists only if System configurationAt:#GemConvertArrayBuilder is false)
CascadeMessage = UnaryMessage | BinaryMessage | KeyWordMessage
CurlyArrayBuilder = '{' [ AExpression { '.' AExpression } ] '}'
Expression = Primary [ Message { ';' CascadeMessage } ]
KeyWordMessage = [ EnvSpecifier | RubyEnvSpecifier ] KeyWordPart { KeyWordPart }
KeyWordPart = KeyWord Primary UnaryMessages { BinaryMessage }
KeyWordPattern = KeyWord VariableName { KeyWord VariableName }
KeyWordPragma = PragmaPair [ PragmaPair ]
Literal = Number | NegNumber | StringLiteral | CharacterLiteral |
SymbolLiteral | ArrayLiteral | SpecialLiteral | ByteArrayLiteral
Message = [ UnaryMessages ] [ BinaryMessages ] [ KeyWordMessage ]
MessagePattern = UnaryPattern | BinaryPattern | KeyWordPattern
Method = MessagePattern [ Primitive ] MethodBody
MethodBody = [ Pragmas ] [ Temporaries ] [ Statements ]
NegNumber = '-' Number
Operand = Path | Literal | Identifier
Operator = '=' | '==' | '<' | '>' | '<=' | '>=' | '~=' | '~~'
ParenStatement = '(' Statement ')'
ParenTerm = '(' AnyTerm ')'
Pragma = '< PragmaBody '>'
PragmaBody = UnaryPragma | KeyWordPragma
Pragmas = Pragma [ Pragma ]
Predicate = ( AnyTerm | ParenTerm ) { '&' Term }
Primary = ArrayBuilder | CurlyArrayBuilder | Literal | Path | Block |
SelectionBlock | ParenStatement | VariableName
Primitive = '<' [ 'protected' | 'unprotected' ] [ 'primitive:' Digits ] '>'
PragmaPair = ( KeyWordNotPrimitive | BinarySelector ) PragmaLiteral
UnaryPragmaIdentifier is any Identifier except 'protected', 'unprotected',
'requiresVc'
PragmaLiteral = Number | NegNumber | StringLiteral | CharacterLiteral |
SymbolLiteral | SpecialLiteral
SelectionBlock = '{' Parameter '|' Predicate '}'
Statement = Assignment | Expression
Statements = { [ Pragmas] { Statement '.' } } [ Pragmas ] [ ['^']
Statement ['.' [ Pragmas ] ]]
Temporaries = '|' { VariableName } '|'
Term = ParenTerm | Operand
UnaryMessage = [ EnvSpecifier ] Identifier
UnaryMessages = { UnaryMessage }
UnaryPattern = Identifier
UnaryPragma = SpecialLiteral | UnaryPragmaIdentifier
GemStone Smalltalk lexical tokens are shown in Figure A.2. No white space is allowed within lexical tokens.
BinaryExponent = ( 'e' | 'E' | 'd' | 'D' | 'q' ) ['-'] Digits
BinarySelector = SelectorCharacter [SelectorCharacter]
Character = Any Ascii character with ordinal value 0..255
CharacterLiteral = '$' Character
Comment = '"' { Character } '"'
DecimalExponent = ( 'f' | 'F' ) ['-'] Digits
Digit = '0' | '1' | '2' | ... | '9'
Digits = Digit {Digit}
EndOfSource = the end of the method source string
Exponent = BinaryExponent | DecimalExponent | ScaledDecimalExponent |
FixedPointExponent
EnvSpecifier = '@env' Digits ':'
(no white space before or after Digits)
FractionalPart = '.' Digits [Exponent]
FixedPointExponent = 'p' [ ['-'] Digits ]
Identifier = SingleLetterIdentifier | MultiLetterIdentifier
KeyWord = Identifier ':'
KeyWordNotPrimitive is any KeyWord other than 'primitive:'
Letter = 'A' | 'B' | ... | 'Z' | 'a' | 'b' | ... | 'z' | '_'
MultiLetterIdentifier = Letter ( Letter | Digit ) { Letter | Digit }
Number = RadixedLiteral | NumericLiteral
Numeric = Digit | 'A' | 'B' | ... | 'Z'
NumericLiteral = Digits ( [FractionalPart] | [Exponent] )
Numerics = Numeric { Numeric }
Parameter = ':' VariableName
(white space allowed between : and variableName)
Path = Identifier '.' PathIdentifier { '.' PathIdentifier }
PathIdentifier = Identifier | '*'
RadixedLiteral = Digits ( '#' | 'r' ) Numerics
ScaledDecimalExponent = 's' [ ['-'] Digits ]
ScdExponTerminator = '"' | WhiteSpace | ',' | ')' | ']' | '}' | '.' |
';' | EndOfSource
SelectorCharacter = '+' | '-' | '\' | '*' | '~' | '<' | '>' | '='
| '|' | '/' | '&' | '@' | '%' | ',' | '?' | '!'
SingleLetterIdentifier = Letter
SpecialLiteral = 'true' | 'false' | 'nil' | '_remoteNil'
StringLiteral = "'" { Character | "''" } "'"
Symbol = Identifier | BinarySelector | ( Keyword { Keyword } )
SymbolArrayItem = Identifier | ( KeyWord { KeyWord } )
SymbolLiteral = '#' ( Symbol | StringLiteral )
VariableName = Identifier