Essence# Syntax: Method Declarations

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

Essence# Syntax: Method Literals

A method literal is a method declaration surrounded by enclosing syntax so that it can be embedded as a literal value in an Essence# expression.

The EBNF for method literals:

MethodLiteral       = "[", MethodDeclaration, "]";

 

A method literal must be enclosed between a single beginning “[” character and a single ending “]” character, making its syntax rather similar to that of a block. The key difference between the syntax of a block and the syntax of a method literal is that the construct that immediately follows the beginning “[” character must be unambiguously a method header. And that, in fact, is one of the reasons that the “##” (method header) tokenexists, and is required in some cases, but optional in others: When the “##” token is required, it’s because its absence would create syntactical ambiguity, such that it would not be possible for the parser to distinguish ablock from a method literal.

For more information on the syntax of a method declaration, please see the article on that topic.

Methods defined in Essence# class libraries declare methods as method literals, instead of as method declarations that are the root of their respective parse trees. Using method literals for that purpose obviates any need to encode method names as filenames; or alternatively, it obviates any need to define a special syntax for dealing with sequences of method declarations, or for syntactically embedding method declarations inside of class declarations. So there’s no need for a special “file in” syntax, nor any need for a special parser that can consume a special “file in format.”.

That said, the ANSI standard does require that a conforming implementation support the Smalltalk Interchange Format. Essence# does not currently support that format, but will do so before it leaves beta.

Using Class Specifications

A method declaration may optionally use a class specification construct–but only if a method header token is also used. That means a method literal may also use a class specification construct, since its syntax is defined as an embedding of a method declaration enclosed in between the tokens “[” and “]”.

The presence or absence of the class specification construct may change the behavior of the compiler:

If there is no class specification in the method header, then whether or not the compiler will attempt to bind non-local variable references depends upon how the compiler is invoked. If the compiler is not provided with a binding context for non-local variables when it’s invoked, and if there is no class specification in the method header to provide one, then the compiler won’t check whether any references to non-local variables might be undeclared (however, that check is always performed whenever a method is added to a class or trait.)

On the other hand, if the method header includes a class specification, then the compiler will always attempt to bind references to non-local variables, using whichever class is specified by the class specification construct as the binding context. In that case, any undeclared variables will be treated as compilation errors.

In other words, the compiler interprets the presence of a class specification construct in a method header as a command to verify that there would be no undeclared variables referenced by the method it’s compiling, if that method were to be added to the specified class. Conversely, it interprets the absence of a class specification as a command to defer any such checks until the method is actually added to a class or trait.

When compiling either self expressions or “do its” (initializers or scripts,) the compiler is not configured to provide any default binding context for method declarations–and therefore is also not configured to do so formethod literals. That’s because there’s no way to know a priori what the “right” binding context might be in such cases.

Since methods are checked for any references to undeclared variables when they are added to a class or to a trait (which is usually the proper time, because that’s when the right binding context is known absolutely,) there are no system integrity issues raised by this binding paradigm. And that’s why the method literals in “methods.instance” and “methods.class” files don’t use class specification constructs in their method headers. There’s no need, really.

However, there are compilation use cases other than compiling “methods.instance” and “methods.class” files. And some of those use cases do require that the compiler bind all variable references during initial compilation–which is why class specification syntax is present as on option for method headers.

New Release Of Essence#: Nīsān (Alpha Build 22)

The Nīsān release introduces full support for ANSI-Standard Dates and Times into Essence#. It also fixes some important bugs.

Nīsān is the name of the first month of the eccelesiastical Hebrew Calendar (the name of the first month of the secular Hebrew Calendar is Tishri.) [We’re still using a Biblical naming scheme, because we’re still in alpha. However, we’re (hopefully) only 2-3 releases away from going to beta, which will happen after we achieve full compliance with the ANSI Standard.]

In addition to what’s required by the ANSI Standard with respect to times, dates and durations of time, convenience methods were added to class Number that enable the creation of Durations by sending messages such as #days, #hours, #minutes, #seconds, #milliseconds and #microseconds to numbers.

As you may or may not be aware, I’m not only the author Essence#, I’m also the author of the Chronos Date/Time Library. In spite of that, I’ve added very little time/date functionality in this release that wasn’t either required by the ANSI Standard or provided by the relevant classes and methods in the .Net Base Class Library.

About 80% of the users of a programming language just don’t need anything more in the way of time/date support beyond what is required by the ANSI Standard. And those who do only need it about 20% of the time. So it doesn’t make good sense to include anything like the Chronos Date/Time Library in the “standard library” for any programming language: It’s overkill for most people, most of the time.

But it does make sense to include the Chronos Date/Time Library as an extension library. But the time for that is not yet.

Download Nīsān (Alpha Build 22)

To download the latest release, navigate to the DOWNLOADS page on CodePlex. There’s a download link in the upper left corner of the DOWNLOADS page, labeled Essence#_Nisan_Setup.exe. Using it will get you a program that will install the Nīsān release of Essence# to any location you choose on your computer. Please see the documentation page on the CodePlex site for more information on how to use Essence#, such as the installation instructions and instructions on how to run scripts written in Essence#.

The Nīsān release includes changes and additions to the Essence# Standard Library–which will be installed by the installer program attached to this release, or which may be obtained separately from GitHub.

One of the utility scripts that aid in developing Essence# code was changed in this release, and a bug in one of the example scripts was fixed. Other that that, there were no other changes to any of of the scripts, and no new scripts were added. For more information on the scripts, please see the documentation.

Multiple Object Spaces In Essence#

What is an Object Space?

An object space is an object that encapsulates the execution context of an Essence# program. It is also responsible for initializing and hosting the Essence# run time system, including the dynamic binding subsystem that animates/reifies the meta-object protocol of Microsoft’s Dynamic Language Runtime (DLR.)

Any number of different object spaces may be active at the same time. Each one creates and encapsulates its own, independent execution context. The compiler and the library loader operate on and in a specific object space. Blocks and methods execute in the context of a specific object space. Essence# classes, traits and namespaces are bound to a specific object space. Even when a class, trait or namespace is defined in the same class library and the same containing namespace, they are independent and separate from any that might have the same qualified names that are bound to a different object space.

In spite of that, it is quite possible for an object bound to one object space to send messages to an object bound to a different object space. One way to do that would be to use the DLR’s hosting protocol. That’s because an Essence# object space is the Essence#-specific object that actually implements the bulk of the behavior required by a DLR language context, which is an architectural object of the DLR’s hosting protocol.

The C# class EssenceSharp.ClientServices.ESLanguageContext subclasses the DLR class Microsoft.Scripting.Runtime.LanguageContext, and thereby is enabled to interoperate with the DLR’s hosting protocol. But an instance of EssenceSharp.ClientServices.ESLanguageContext’s only real job is to serve as a facade over instances of the C# class EssenceSharp.Runtime.ESObjectSpace. And EssenceSharp.Runtime.ESObjectSpace is the class that reifies an Essence# object space.

So, if you are only interested in using Essence#, and have no interest in using other dynamic languages hosted on the DLR, there is no need to use a DLR language context in order to invoke the Essence# compiler and run time system from your own C#, F# or Visual Basic code. You can use instances of EssenceSharp.Runtime.ESObjectSpace directly. The only disadvantage of that would be that using other DLR-hosted languages would then require a completely different API (e.g, using an IronPython library from Essence# code requires using the DLR hosting protocol, and hence requires using a DLR language context).

The advantages of using instances of EssenceSharp.Runtime.ESObjectSpace directly would be a much richer API that is far more specific to Essence#.

You can get the object space for the current execution environment by sending the message #objectSpace to any Essence# class (even to those that represent CLR types.) And the Essence# Standard Library includes a definition for an Essence# class that represents the Essence#-specific behavior of instances of the C# class EssenceSharp.Runtime.ESObjectSpace. It’s in the namespace CLR.EssenceSharp.Runtime, and so can be found at %EssenceSharpPath%\Source\Libraries\Standard.lib\CLR\EssenceSharp\Runtime\ObjectSpace.

There are many ways that Essence# object spaces might be useful. One example would be to use one object space to host programming tools such as as browsers, inspectors and debuggers, but to have the applications on which those tools operate be in their own objects spaces. That architecture would isolate the programming tools from any misbehavior of the applications on which they operate–and vice versa.

To get additional insight into the concept of object spaces and how they might be used to good effect, the paper Virtual Smalltalk Images: Model and Applications is highly recommended.

New Release Of Essence#: Nile-1 (Alpha Build 21)

The Nile-1 release fixes bugs and improves and/or corrects internal documentation.

There are no new features. However, extensive updates have been made to the documentation section of the Essence# CodePlex site.

Download Nile-1 (Alpha Build 21)

To download the latest release, navigate to the DOWNLOADS page on CodePlex. There’s a download link in the upper left corner of the DOWNLOADS page, labeled Essence#_Nile-1_Setup.exe. Using it will get you a program that will install the Nile-1 release of Essence# to any location you choose on your computer. Please see the documentation page on the CodePlex site for more information on how to use Essence#, such as the installation instructions and instructions on how to run scripts written in Essence#.

The Nile-1 release includes a few bug fixes to the Essence# Standard Library–which will be installed by the installer program attached to the release, or which may be obtained separately from GitHub.

Neither the example scripts nor the utility scripts that aid in developing Essence# code have been changed in this release. And no new scripts were added. However, the existing scripts have now been documented

Final note: If you have a version of Essence# prior to Alpha Build 17 (Philemon) and have also written your own Essence# code using that release that you would like to keep using with this release, then please be sure to also read the release notes for Alpha Build 17. There have been significant changes to the format of the Essence#Standard Library => Philemon (Alpha Build 17).

Using Reflection On The Essence# Code Base

Just because there is as yet no Essence# GUI library, and therefore no native Essence# code browsing tools, doesn’t mean that the intrinsic reflecting capabilities of Essence# can’t be used. In fact, scripts are provided in the shared scripts folder that provide at least some of the functionality traditionally provided by code browsers:

ShowAllMethods: The ShowAllMethods.es script can be used to print out the names and declaring class or trait of all the methods of a class or trait. The subject class or trait must be passed in as an argument, as in the following example which will print out the names and declaring class or trait of all the methods of class Array to the Transcript:

es ShowAllMethods -a Array | more

ShowAllMessagesSent: The ShowAllMessagesSent.es script can be used to print out all the messages sent by each method of a class or trait. The output is cross-referenced by the sending methods, and each such method specifies the class or trait that declares it. The subject class or trait must be passed in as an argument, as in the following example which will print out the names of all the messages sent by each method of class Array to the Transcript:

es ShowAllMessagesSent -a Array | more

ShowAllSenders: The ShowAllSenders.es script can be used to print out the names and declaring class or trait of all the methods in the object space that send a specified message. The subject message selector must be passed in as an argument, as in the following example which will print out to the Transcript the names and declaring class or trait of all the methods in the object space that send the message do:

es ShowAllSenders -a #do: | more

ShowAllSendersInHierarchy: The ShowAllSendersInHierarchy.es script can be used to print out the names and declaring class or trait of all the methods of a specified class that send a specified message. The subject message selector and the subject class or trait must both be passed in as arguments, as in the following example which will print out to the Transcript the names and declaring class or trait of all the methods of OrderedCollection that send the message do:

es ShowAllSendersInHierarchy -a #do: -a OrderedCollection | more

ShowUnimplementedMessages: The ShowUnimplementedMessages.es script can be used to print out the names of all the messages sent by the methods of a specified class or trait to the pseudo-variable self for which the specified class or trait has no implementing methods. The subject class or trait must be passed in as an argument, as in the following example which will print out to the Transcript the names of any messages sent to self by the class OrderedCollection that send messages for which OrderedCollection has no implementing methods:

es ShowUnimplementedMessages -a OrderedCollection | more

Note: Classes that represent CLR types typically have virtual Essence# methods that don’t need to be formally declared, because the Essence# dynamic binding system will automatically bind to and invoke the methods of a CLR type, provided those methods have less than two parameters. Messages sent in order to invoke such methods of CLR types will unavoidably show up as “unimplemented messages” when using the ShowUnimplementedMessages.es script.

ShowTraitUsageConflicts: The ShowTraitUsageConflicts.es script can be used to print out the name and declaring trait of all methods which were excluded from a trait usage expression due to the fact that methods with the same selectors were declared by two or more of the traits combined in a trait usage expression. The subject class or trait must be passed in as an argument, as in the following example which will print out to the Transcript methods excluded from the trait usage of ReadStream because two or more of the traits used by ReadStream had the same method selector:

es ShowTraitUsageConflicts -a ReadStream | more