Overview of the Gilda Programming Language

                 by Bradley Berg       February 5, 2014

          Copyright 2014, Bradley Berg; all rights are reserved.



I. INTRODUCTION

Gilda is a general purpose procedural programming language intended
for writing production quality programs.  Constructs were selected
based on clarity and practicality.  Features were kept to a minimum
and the resulting notations are easy to learn.  Notations were devised
to reduce overhead to write reliable and portable programs.  Special
attention was given to improving software maintainability; particularly
by including provisions for managing side effects.

The language design evolved by incorporating results from use on a
substantial code base.  Alternate choices were tried in small experiments
and more promising notations were tested on the larger code base.  New
features were not added unless they significantly reduced the size of
the code base or resulted in code that was easier to work with.

This overview briefly describes the language and will give you a good
idea how Gilda programs are constructed.  It is not intended to be a
programming guide or tutorial.  Instead it is a quick introduction to
some of the more interesting language features.  Many aspects of the
language are quite conventional and are not mentioned here.


II. METHOD PREAMBLE

Method preambles are heart of the design of any procedural language.
They define interfaces for libraries and program components.  Programmers
reference them routinely to communicate with each other throughout all
phases of software development.

Preambles contain a set of declarations while method bodies consist of
executable statements.  Method signatures are part of the preamble and
define its external interface; how parameters are passed and its exception
behavior.  The remainder of the preamble declares local and global
variables used in the body.


A.  Method Signature Parameters

Parameters are declared with Entry, Exit, or Change keywords.  Entry
parameters declare inputs and can not be modified by the method.
Outputs are declared with Exit parameters.  Change parameters can be
modified by the body and are input and output parameters.

Gilda signatures were designed to support automatic generation of
documentation, menus, and command line programs.  Each parameter
declaration has a name, type, an optional default value, and an
optional comment.  In this example the comment is proceeded by the
colon and is associated with the parameter so it can be used by
utilities that generate code and documentation.

    entry Size = 0  byte     :The length of each widget.


Strings are an intrinsic type rather than composed from arrays or
an add on class.  As a built in type, their definition and use is
consistent over all Gilda source code.  Since strings are so ubiquitous
this means signatures are consistent and tools will know their semantics
so they can support signatures with strings.  Gilda includes several
functions to manipulate strings as well.

Intrinsic variable length strings are not only convenient, but can be
optimized by the compiler.  A high performance internal representation
for strings is used and a dedicated memory manager was devised for
fast string memory management.


B. Exception Signature

Exception handling is the most difficult aspect of programming.
It is essential for writing reliable and robust programs.  Writing
code that behaves well under failing conditions is not only hard,
but can result in programs that are much larger and complex.

Gilda employs a novel exception mechanism instead of the conventional
try-throw-catch model.  The primary difference is that exceptions are
not named.  Instead the data state is used to raise and disambiguate
exceptions.  Named exceptions do not scale well and are difficult to
change.  They introduce unnecessary control coupling similar to the
way that program branch labels do on Goto statements.

Program faults are exceptions that are unexpected under correct
program operation.  These are typically due to internal integrity
checks or hardware failures.  The remainder of exceptions are
managed exceptions.  In any particular case the programmer decides
which category to use.

The two types of exceptions have distinct control paths.  Any method
can potentially fail due to a fault.  The programmer can not be aware
of many faults as they arfe commonly due to programmer error.  For
this reason faults are not part of the signature.

A method can be declared with the Safe keyword to denote that it
does not raise any managed exceptions.  Faults can still be raised
even if a method is exception safe.

A stronger restriction is denoted by the keyword Pure; which means
that the method will only modify Change or Exit parameters.  It is
also prevented from changing global variables, but it may read them.
Pure methods are also restricted from performing physical input and
output operations.  The Safe and Pure declarations let maintainers
know that these methods have limited or no side effects to simplify
program analysis by either developers or tools.

When a method can raise managed exceptions it will either have no
tag or can use the keyword Signal.  In the first case handlers up
the call chain disambiguate the exception by context.  The Signal
keyword indicates that additional context is saved that can be
accessed only by handlers.

Designated members of the class containing the method are denoted as
Signal variables.  Signals are used to declare more complex exceptions
or exceptions that are exported from a library.  Exception state is
contained in data object classes rather than as named exceptions
representing the control state of a method.  Since Signal variables
are declared in classes thay can also be inherited.

Note that there is information about specific exceptions that is not
specified in a method signature.  In Gilda changes to exceptions
impact handlers or class declarations and do not affect either the way
that methods are specified or called.  This avoids the ripple effect
that occurs up the call chain with changes in exception behavior.


C. Preconditions

Method signatures also include preconditions; which are a set of
conditions that must be met before a method body can be executed.
They are useful for validating input parameters and global variables.
When calling methods preconditions specify constraints that help
specify how to set up parameters.

The precondition block can also be designated as Safe or Pure.
This means that as long as the preconditions are met the method
will not raise managed exceptions.  The only managed exceptions
that can be raised are specified by the preconditions.

When a precondition fails it can either raise a fault or a managed
exception.  Preconditions are therefore another way to raise an
exception in a method.  Additionally a context string can be
provided that can be used by handlers.

   precondition pure     :A pure method if preconditions are met.
      Name <> "";         ERROR:  A name was not given.
      Size > 3  fault;    CHECK:  The size must be over 3.
      Id <> 0  pass Id;   ERROR:  The ID is unknown.
   .                     :A period ends a precondition block.


The comments on conditions after the semicolon are also passed to
handlers as a text string.  This is very convenient for reporting
simple messages and documents the code as well.

In the example above the Name is first tested to see if it is set
and a managed exception is raised if it fails.  Then an integrity
check is made on the Size variable and a program fault is raised if
it is not met.  Finally if the Id is not set the exception context
string is set with to the signed decimal value of the Id and a
managed exception is raised.  Handlers can then access the context
variable.


D. Functions and Subroutines

Methods can be invoked either as functions or subroutine calls.
Functions are always implicitly pure.  They can not change their
input parameters or any globals and can not perform physical input
or output.  Since functions appear in expressions this means that
expressions and conditions are free of side effects.

When maintaining code constraints on side effect make it much easier to
read.  Furthermore, it is well known that subtle bugs can be introduced
in programs when these kinds of side effects are permitted.  Constructs
that use expressions like the right hand side of assignments and
conditional statements are subsequently side effect free.

    function DO_SOMETHING:  Begin a function declaration.

     entry Name        string,   &Inputs are declared first.
           Value = ""  string    :Optional parameters have defaults.

      exit Result      string    :The last parameter is the output.


This function could be called as:  R = Do_Something( N, V )

Methods on the other hand are assumed to raise managed exceptions by
default.  For other exception behavior they need to be explicitly
declared with Pure, Safe, or Signal keywords.


    method DO_BY_CALL  safe:  This method does not raise exceptions.
                           :  It may perform I/O or change globals.

      exit Result      string   :Parameters are declared in any order.

     entry Name        string

    change Value = ""  string   :Change parameters are only in methods.


This method could be called as:  DO_BY_CALL  R, N, V


III.  METHOD BODY

The syntax for Gilda was designed to place pseudo code comments to the
right of the code.  In many languages this is unattractive and is hence
is unpopular.  Commenting code bodies in general is not always popular.
If you do like to comment your code pseudo code works quite well with
Gilda statements.

Another consideration is that the notation was designed to make it easily
formatted by programming tools.  Other than quoted text the code is not
sensitive to case.  You can enter the code as lower case text and an
editor with a built in pretty printer can change the case and indent
code blocks.


A. Control Statements

Gilda includes typical conditional branch (If) and loop constructs along
with iterative loops.  Several subtle design flaws in iterative loops
are avoided by carefully defining loop semantics.  The programming
errors that result from these flaws are in turn avoided.

   DO <Variable> = <Start>  to  <Maximum>  [ by <Step> ]


The chief problem in integer iterators is proper termination.  Integer
iterators can unexpectedly wrap if the maximum is near the maximum
integer size.  Floating point iterators can suffer from accumulated
epsilon errors that can differ over multiple platforms.  Gilda iterators
are specified so that iterators behave consistently and as expected.

For convenience loop constructs are include for scanning characters in
a string and elements of an array vector.  A sequence loop construct is
also included.  Sequences implement the idiom - begin; iterate; and
terminate.

   sequence  REVERSE  pure:  Return characters in a string in reverse.
    entry Text       string       :Input string.
     exit Character  string       :A sequence of characters.

   DO I = length( Text )  to  1:   DO over the string in reverse,
      Character = byte( Text, I );    Get the next character.
      YIELD;                          Return it and resume here next call.
   -                                 :Loops end with a dash.

   return;                         Terminate the sequence.


This code snippet shows how the sequence can be used:

   R = "";                         Clear the result.

   DO C  from  Reverse( Name ):    DO over the Name in reverse,
      R != C;                         Append each character.
   -

After the loop the variable R will be set to the string in reverse.  A
short hand sequence notation is available for data structures.  Each
structure can declare a default sequence.  A common usage is in container
classes.  For example, a queue class might declare a default sequence to
iterate over the queue from front to back.


   DO E  from  In_Queue:     DO over elements in the queue In_Queue,
      PRINT  E;                 List each element.
   -

A rationale for the design of the loop constructs is presented in the paper,

B. Assert Statement

Assertions are used to raise exceptions and work the same way as
preconditions.  When an assertion is not met control is passed to
handlers.  Handlers are coded after method bodies and are lexically
distinct.  Separating them out from bodies results in clearer code
and avoids extensive re-factoring when modifying exception behavior.

A detailed analysis of Gilda's exception mechanism is in the paper,

C. Trace Statement

For convenience Trace statements are used for printing out values.
Using print statements for debugging may not be state of the art,
but is widely used.  This simple example illustrates the usage.

    trace`on  I, Id, Text


The "on" switch enables the Trace statement.  It can be removed to ignore
the statement.  It is often useful to leave disabled Traces in the code
for future debugging.  Trace statements can be globally disabled as
well.  Output is written to the standard error stream, but may also be
directed to a log file.  It is prefixed with the method name and line
number of the statement.  The output might look like:

   My_Method@42: I = 6 Id = Active Text = x y z


IV.  CLASSES

The Gilda class structure constitutes an implicit build system.  The
compiler can build complete programs and libraries without the use of
an external build system (e.g. Make files).

Name spaces are implemented as libraries.  That is each library has
its own name space; which is declared in the library`s root class.
Building libraries and resolving global name collisions are managed
using the same notation.


V.  CONCLUSION

Over the years thousands of programming languages have been created.
There is even more diversity of opinion about what makes an appealing
programming language.  Regardless of how well versed you are in
software engineering or language design, there is no substitute for
working with a language on practical programs.  This is particularly
true of languages with novel constructs such as Gilda's exception
handling.  Hopefully this introduction will peak your curiosity enough
to try it out.