A method declaration is the syntactical construct used to define a method, including its name and its logic. In Essence#, the name of a method is typically referred to as its method selector; or even just its selector.
The EBNF for a method declaration:
MethodDeclaration = [MethodHeaderToken, [ClassSpecification]],
MethodHeader, ExecutableCode;
MethodHeaderToken = OptionalWhitespace, "##", OptionalWhitespace;
MethodHeader = UnaryMethodHeader |
BinaryMethodHeader |
KeywordMethodHeader;
UnaryMethodHeader = UnaryMessageSelector;
BinaryMethodHeader = BinaryMessageSelector, OptionalWhiteSpace, BindableIdentifier;
KeywordMethodHeader = KeywordMethodHeaderSegment,
{Whitespace, KeywordMethodHeaderSegment};
ClassSpecification = QualifiedIdentifier,OptionalWhitespace,">>", OptionalWhitespace;
At the beginning of a method declaration, the parser will recognize “##” (two immediately-adjacent hash characters) as a lexical token called a method header token, and will interpret the token to mean “what follows is a method header.”
Whitespace is permitted, but not required, both preceding and following the method header token.
A method header token may optionally appear as the first token of any method declaration; but it is not required if the method declaration is the root of the parse tree.
However, if a method header token occurs as the first (non-whitespace) token following a “[” token, then the parser will have no choice but to interpret what follows as the remaining tokens of a method literal (which must be terminated by a “]” token eventually.) And in that case, the “##” (method header) token may be required:
If the initial token following a “[” (BlockBegin) token is either a binary message selector or a keyword, then the source code enclosed within the “[” and “]” tokens will be parsed as a method declaration even though there is no leading method header token, and the entire construct (including the “[” and “]” tokens) will be interpreted as a method literal, and not as a block literal. So in either of those cases, no method header token is required.
However, if the first token following a “[” token is a unary message selector (which might instead just be a variable name,) and if there is no leading method header token in between the “[” token and the unary message selector, then the parser will not interpret the construct as a method literal, but will instead interpret it as a block. So, when a method declaration occurs as part of a method literal, and said method declaration has a unary method header, the only way to get the parser to interpret the construct as a method literal, and not as a block, is to use a method header token as a prefix to the unary method header.
Note: The method header token will also be required as a prefix to a binary method header, if the binary selector is the “|” (vertical bar) token. That constraint is required in order to avoid syntactical ambiguity, due to the fact that a vertical bar token may also be the initial token of a variable declaration list.
If and only if a method header is preceded by the “##” (method header) token, the name of the class which is to be used as the environment for binding variable references when compiling the method may be specified preceding the method header. But in that case, the token “>>” must then be used as a separator between the class name and the method header.
Whitespace is permitted but not required in between the class name and the “>>” token, and in between the “>>” token and the method header.
Following the method header there must be an executable code construct. An executable code construct defines the method’s logic. Colloquially, an executable code construct is referred to as a method body. A method bodyhas the same exact syntactical structure as a block body.
There are three different types of method header: A unary method header, a binary method header and akeyword method header.
A method declaration with a unary method header must be invoked using a unary message.
A method declaration with a binary method header must be invoked using a binary message.
A method declaration with a keyword method header must be invoked using a keyword message.
Examples of method declarations using all three types of method header are shown below (none of which have a method header token as a prefix):
Method declaration using a unary method header:
printString
| stream |
stream := String new writeStream.
self printOn: stream.
^stream contents
Method declaration using a binary method header:
@ y
^Point x: self y: y
Method declaration using a keyword method header:
displayOn: aGraphicsContext at: aPoint
aGraphicsContext displayString: self at: aPoint
If a method header token (“##”) precedes it, then a class specification construct may optionally precede any of the three types of method header. If present, the class specified by the class specification will be used by the compiler as the behavioral context in which the method will be compiled. In other words, the instance variables defined by the specified class, the class variables defined by the specified class and the global variables imported by the specified class will be used to bind any variables referenced by the method that aren’t either method parameters or local variables.
Here are the same three method declarations constructed to have an optional method header token and class specification construct as a prefix to the method header:
Method declaration using a unary method header and an optional class specification:
## Object>>printString
| stream |
stream := String new writeStream.
self printOn: stream.
^stream contents
Method declaration using a binary method header and an optional class specification:
## Number>> @ y
^Point x: self y: y
Method declaration using a keyword method header and an optional class specification:
## String>>displayOn: aGraphicsContext at: aPoint
aGraphicsContext displayString: self at: aPoint
Any method declaration (whether it uses a unary method header, a binary method header or a keyword method header, and whether or not it uses an optional class specification) may optionally begin with a method header token. The reason the method header token is optional is because its purpose is either to separate one method declaration from another in a sequence of method declarations, or else to distinguish a method literal from ablock. Outside of those two cases, it has no purpose, function or meaning. Its presence or absence has no effect on the semantics of the method.
Here’s an example showing two method declarations separated by an intervening method header token:
Duration>>asDays
^self ticks / TicksPerDay
##
Duration>>asHours
^self ticks / TicksPerHour