Programming Language for Old Timers


by David A. Moon
February 2006 .. September 2008

Comments and criticisms to dave underscore moon atsign alum dot mit dot edu.


Previous page   Table of Contents   Next page


Annotations

Annotations are like machine-readable comments. They are introduced by colon, whereas ordinary comments are introduced by semicolon. Annotations can only appear in specific places in the grammar, generally in definition statements. Annotations allow attaching "extra" information to a definition that can be used by the compiler to enable optimizations or by a development environment to assist the developer. Annotations are not documentation; the self-documentation for a definition is simply any comments immediately preceding the definition.

You can annotate classes, slots, functations (both fun and defun), definitions (def), and any user-defined constructs that allow annotations. An annotation can look like a name or a function call. The argument expressions in a call-like annotation must be literals or names, which are implicitly quoted.

The syntax for annotations is defined as:

;; Return a list of lists of function and argument list
defparser annotations : { ?:name [ ( ?argument is annotation-argument & , ^^ ) ] & , ^^ }+ =>
  map(fun(name, args)
        def fname = name-in-context(":" + name, name)
        list(known-function(fname, tokens), args or list()),
      name, arguments)

;; An argument to an annotation must be a literal, a name that is
;; not punctuation, or a name quoted by backslash.
defun parse-annotation-argument (tokens is token-stream,
                                 indentation is integer,
                                 error? is boolean)
  def token = tokens.next
  if token = #\\
    tokens.advance
    def token2 = tokens.next
    if token2 is name
      tokens.advance
      token2
    else if token2 is string
      tokens.advance
      name(token2)
    else
      wrong-token-error(tokens, "a name or string")
  else if token is name and not punctuation?(token) or
          token is literal
    tokens.advance
    token
  else
    wrong-token-error(tokens, "a literal, a name, or a backslash followed by a name or a string")

defun known-function (name is name, tokens is token-stream) is function
  def definition = known-definition(name)
  if definition is function
    definition
  else
    syntax-error(tokens, #"No known function for ?name")

The annotation mechanism is completely user-extensible. An annotation is implemented by calling a function whose name is the annotation name prefixed with colon and whose arguments are the object being defined and any additional arguments specified in the annotation. The method executed can return a replacement object or side-effect the object and return its first argument. Annotations are called during macro expansion.

For example, the sealed annotation for methods could be defined in one of these two ways:

defun :sealed(m is method-definition)
  m.sealed? := true
  m

defun :sealed(m is method-definition)
  sealed-method-definition(m.name, m.method)

The following helper function applies annotations to an object. It is invoked during macro expansion of a definition statement.

defun apply-annotations(annotation-list, object)
  if annotation-list
    reduce(fun(annotation, object) (annotation[0])(object, annotation[1]...),
           object, annotation-list)
  ;; No annotations, just return the original object
  else object


Previous page   Table of Contents   Next page