TYPENAME PARENT PARTS TYPEMETHODS INSTANCEMETHODS REPLY SIGNAL EXCEPT OTHERWISE TRUE FALSE NIL ASM CALL ON
For example, to print an integer in a field width of 5 characters followed by a new line character:
This will right-justify the value of 'i' in a field 5 spaces wide and make 'print:' work like 'printLine:' using the newline character.newMethod { i -> 22. system print (i asString "%5d\n"). }.
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
do: is an Array method which will assign each item to 'name' and execute the statements in the block.newMethod { names -> ["Pete","Dorothy","Sly","Rex","Barb"]. names do { |name:String| system printLine name. }. }
Arrays do not have to be homogeneous. For example,
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.newMethod { names -> ["Pete",5,"Sly",4.5,"Barb"]. names do { |obj:Object| system printLine (obj asString). }. }
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:.
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. 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.newMethod { anArray -> [3.14159,"data"]. i -> 1; r -> 1.0. pi -> anArray at i:Real. area -> (pi * 2.0)*(r ^ 2.0). }
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:
No error message will be generated and it will execute correctly at runtime if the method exists. If not, you will hear about it!newMethod { anArray -> [3.14159,"data"]. i -> 1; r -> 1.0. pi -> anArray at i. area -> (pi:? * 2.0)*(r ^ 2.0). }
For example, a String method to convert the strings "true" or "false" to their boolean equivalents looks like:
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.asBoolean REPLY Boolean { (me =* "true") then { REPLY TRUE. } else { (me =* "false") then { REPLY FALSE. } else { SIGNAL "Expected TRUE or FALSE". }. }. }
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. }
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.
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:
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.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. }. }
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.
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:':do aBlock:Block { 1 to (me size) do { | i:Integer | aBlock doWith [me at i]. }. }
A judicious choice of keywords for your method name will make it easy to remember and provides a measure of self-documentation.swap source:Integer with dest:Integer { obj -> me at source. me at source put (me at dest). me at dest put obj. }