Programming Fundamentals

In this chapter, you will get more detailed information about the ABC programming language.

Reserved Words

The ABC reserved words are shown in below and may not be variable names or type names in ABC.

TYPENAME PARENT PARTS TYPEMETHODS INSTANCEMETHODS REPLY SIGNAL EXCEPT OTHERWISE TRUE FALSE NIL ASM CALL ON

Names and Symbols

ABC names consist of a letter followed by a sequence of letters or digits. Names are used for variables and types and are case-insensitive.

Constants

TRUE and FALSE
boolean constants
NIL
a non-object
Integer
a sequence of digits as in '123' or '4'
Real
a sequence of zero or more digits followed by a decimal point followed by one or more digits as in '0.45' or '.4' but not '4.'
String
a sequence of characters surrounded by double quotes as in "test"
Character
an Integer whose value is the ASCII equivalent of a single-quoted character as in 'a' or '5'
Symbol
an Integer whose value is unique to its name. A symbol begins with pound sign followed by a name as in #replace or #Input2. Symbols are case-sensitive.
Both Strings and Character constants may contain escape characters while strings may have format codes. Both escape and format characters are based on C conventions and are shown in Table 1. For example, "test=%d\n" would produce a string with a decimal number and newline character embedded in the string. The format characters may be preceded by an optional minus sign, digit string representing a minimum field width and a period and digit string representing the maximum number of characters printed from a string or the number of digits after a decimal for a real number. For example, "%10s" would pad a string on the left if it were less than 10 characters wide but "%-10s" would pad on the right. "%10.3f" would print a real in a field 10 characters wide and 3 digits to the right of the decimal point.

For example, to print an integer in a field width of 5 characters followed by a new line character:

newMethod { i -> 22. system print (i asString "%5d\n"). }.

This will right-justify the value of 'i' in a field 5 spaces wide and make 'print:' work like 'printLine:' using the newline character.

Table 1. Escape and Format Codes

Name Escape Name Format newline n decimal %d tab t unsigned octal %o backspace b hexadecimal %x return r unsigned decimal %u formfeed f single character %c backslash \ string %s double quote " real(E notation) %e octal ddd real %f

Arrays

An Array is a single dimensional list of zero or more objects. An array is created using square brackets as in

newMethod { names -> ["Pete","Dorothy","Sly","Rex","Barb"]. names do { |name:String| system printLine name. }. }

do: is an Array method which will assign each item to 'name' and execute the statements in the block.

Arrays do not have to be homogeneous. For example,

newMethod { names -> ["Pete",5,"Sly",4.5,"Barb"]. names do { |obj:Object| system printLine (obj asString). }. }

will print out the String representation of each item in the Array. Note that the variable 'obj' executes asString to convert the object to a String. Depending on the actual type of 'obj' at each iteration, the appropriate asString will be called to reply with the String representation of the object.

Names and Variables

Names can refer to types, parts (i.e. elements of an object), system variables, method parameters, block variables or local variables. While parts, system variables, parameters and block variables are explictly declared, local variables are not. The type of a local variable becomes known when it is first assigned (i.e. ->) Thereafter, it may only refer to objects of that type or subtype.

The ABC compiler uses the type of a variable to validate the methods which use it as a receiver. For example, if 'i' is an Integer then 'i + 4' would be valid because the +: is defined for integers. However, 'i printLine s' is invalid because the Integer type does not define printLine:.

System Variables

A number of system variables are available for accessing various global objects. These are described below.

master
the root directory of objects
myTask
the current executing task
oops
the exception object generated when an error occurs
widgets
the array of currently active widgets
system
the system object representing the operating system

Variable Scope

Names can refer to types, parts, system variables, parameters, block variables or local variables. ABC determines the scope of a name according the following order:

  1. Previously declared local variable name
  2. Parameter name
  3. Part name
  4. System variable
  5. Type Name
If ABC cannot find the name it is assumed to be an undeclared local variable.

Typecasting

Objects like arrays are inherently heterogeneous making it difficult to infer the type of an array element when a statement is compiled. For example,

newMethod { anArray -> [3.14159,"data"]. i -> 1; r -> 1.0. pi -> anArray at i. area -> (pi * 2.0)*(r ^ 2.0). }

the actual type of 'pi' can only be determined at run-time. As a result, ABC assumes that the elements are all of type Object. Line 4 would not compile because ABC thinks pi is an Object and Objects don't know how to *:. To overcome this difficulty, you may use the notation ':Typename' at the end of an assignment to declare the result type as in:

newMethod { anArray -> [3.14159,"data"]. i -> 1; r -> 1.0. pi -> anArray at i:Real. area -> (pi * 2.0)*(r ^ 2.0). }

You may also do the same at the end of a REPLY. Note that this merely permits the compiler to verify that any 'pi' are valid Real methods. If your typecast does not agree with the object type at runtime, an error will be reported.

There may be times when you don't actually know the receiver type but are certain that it will execute the method at runtime. You can handle this situation by using the ':?' notation:

newMethod { anArray -> [3.14159,"data"]. i -> 1; r -> 1.0. pi -> anArray at i. area -> (pi:? * 2.0)*(r ^ 2.0). }

No error message will be generated and it will execute correctly at runtime if the method exists. If not, you will hear about it!

Methods

In the previous examples, you worked with single statements or sequences of statements. When you compiled these statements, ABC actually built a method from the statements and ran the method. A method is like a procedure or function in PASCAL or C. The method syntax describes the keywords required to invoke the method, the names and types of any parameters and, optionally, the type of object with which this method will reply or return.

For example, a String method to convert the strings "true" or "false" to their boolean equivalents looks like:

asBoolean REPLY Boolean { (me =* "true") then { REPLY TRUE. } else { (me =* "false") then { REPLY FALSE. } else { SIGNAL "Expected TRUE or FALSE". }. }. }

The first REPLY indicates that asBoolean will return a Boolean result. The 'me' refers to the string receiver. The String =*: performs a case-insensitive string comparison and returns TRUE if the strings are equal and FALSE if not. The second and third replies actually return the appropriate Boolean object. The SIGNAL statement will report an error to ABC.

A similar PASCAL version would look like

FUNCTION asBoolean(me:String):Boolean BEGIN if (CompareStringInsensitive(me,"true")) then asBoolean := TRUE; else if (CompareStringInsensitive(me,"false")) then asBoolean := FALSE; else writeln("Expected TRUE or FALSE"); END;

An example of the use of this method is shown below:

newMethod { s -> "True". bool -> s asBoolean. }

Me

When the statement containing asBoolean is executed, the {it asBoolean} method is run. The predefined local variable 'me' will automatically reference the receiver, namely 's'. In the example, the receiver is then compared to the strings "true" and "false". Since methods are usually only defined as part of type definitions, the type of me is usually that of the defined type. In this case, me is a String.

Replying

Whenever a method is executed, a reply is always given. The action of the REPLY keyword in a statement (not in the Method header) is to specify the reply object and exit the method. The REPLY object must be the same type as, or subtype of, the type specified in the REPLY type of the method. In the example, the method replies with a Boolean TRUE or FALSE which agrees with the 'REPLY Boolean' syntax.

A method need not specify a REPLY type or use a REPLY statement. If no REPLY is specified, a method will reply with its receiver.

Handling Errors

If an error or special condition occurs, an exception is signaled. This can be done explicitly using the SIGNAL statement or implicitly by the ABC system. In the example, the SIGNAL statement will initiate exception processing. The system variable 'oops' will point to the exception which is being reported. The exception object includes a string description of the exception (called the reason) as well as information about the methods which were active when the error occurred.

When an exception is signaled, control passes to the EXCEPT section of the method which is executing. Since the EXCEPT section is always at the bottom of the method, all intervening code will be skipped. The reason is compared (case-insensitively) with any ON reasons listed in the EXCEPT section. If found, the block associated with the matched ON section is executed and the method terminates normally. If the reason does not match any ON section (it will always match the OTHERWISE section), the current method is exited and the caller method's EXCEPT section is searched in a similar manner. If no ON condition matches the reason, the system will automatically report the exception and terminate the task. An example of an except section is shown below:

newMethod { r -> 5.0 / 0.0. system printLine "Finished normally". EXCEPT ON "A divide by zero occurred" { system printLine "Check your denominator". }. OTHERWISE { system printLine oops's reason. }. }

When ABC attempts to divide by zero in the first statement, an exception will be signaled. The message "Finished normally" will not be printed, but control will pass to the EXCEPT section. In this case, the ON string will match the reason and the "Check your denominator" will be printed. If the exception reason doesn't match any ON string, the OTHERWISE block will be executed if there is one.

The SIGNAL keyword requires a string as the first parameter. You may optionally supply additional objects separated by commas to provide specific information about the cause of the SIGNAL. The system variable 'oops' has a part called 'info' which is the Array of objects you supplied and may be accessed by "exception's info" in the ON or OTHERWISE block as needed. The first element of info is the reason and the remaining elements are the SIGNAL-specific objects.

Parameters

Methods may also have parameters as shown in the following example from Type Array which executes a block for all elements of an Array:

do aBlock:Block { 1 to (me size) do { | i:Integer | aBlock doWith [me at i]. }. }

Note that the method name consists of a keyword 'do' followed by a parameter called 'aBlock' which is declared of Type Block. If method parameters are supplied, the method syntax always follows the convention 'key1 p1:T1 key2 p2:T2 ...' as in the Array method 'swap:with:':

swap source:Integer with dest:Integer { obj -> me at source. me at source put (me at dest). me at dest put obj. }

A judicious choice of keywords for your method name will make it easy to remember and provides a measure of self-documentation.