Why these
Combinators?

Have you always been intrigued by Attribute Grammars because they
allow you to:

write language processors in a
compositional way in which

the different aspects of a language may
be textually separated

but you do not like:

to write code for constructing and
inspecting intermediate data structures

the often verbose notation used by other
systems

to learn a new language for describing
semantic functions

the often limited expressibility of
these languages

the sheer size of existing
systems

the absence of any higher-order
facilities

then why not use my simple attribute grammar system?

The UU_AG system is a very simple program that reads a set of
files containing an attribute grammar, in which semantic functions
are described through Haskell expressions. Out of this description
catamorphisms and data type definitions are generated.

I hope this software is useful to you. If you have any comments do
not hesitate to contact me.

Conditions for use

I have been asked about any copyright associated with these files.
Since I do not want to get too deeply involved in all kinds of
legalese I have included a slightly
adapted version of the artistic license that came with my version
of Hugs. It seems to capture nicely what is my intent with this
package.

I would be happy it you would let me
know:

whether you are using the package and
what for

what modifications you are interested
in

what modifications you have made
yourself

whether you think the package is useful
(visiting committees have called the construction of such packages
"not useful"; once they show up again it would be nice if I could
present proof of the contrary)

The Main
Structure of the Input

We start with providing the full grammar of the accepted input, as
expressed by the parsing part of the UU_AG file:

parser = parse pAG

The input consists of a list of separate textual elements, each representing
a small contribution to the complete grammar. Such grammar fragments may even
be distributed over several files, according to the conventions stemming from
the use of the standard UU scanning library

pAG = foldl sem_Elems_One_Elem sem_Elems_No_Elem
<$> pList pElem

Each element in this list:

pElem =

either represents:

a grammar rule definition. Since
each production will lead to one more alternative in the data type
describing the abstract syntax tree we have chosen the keyword
"DATA" to start such an element
with:

sem_Elem_Data <$ pKey "DATA"
<*> pConid <*> pAttrs
<*> pAlts sem_Alt

the definition of some attributes
belonging to certain nonterminals

<|> sem_Elem_AttrsDef <$ pKey "ATTR"
<*> pList pConid <*> pAttrs

the definition of semantic
functions, describing how to compute a specific attribute
out of other attributes:

<|> sem_Elem_Rules <$ pKey "SEM"
<*> pConid <*> pAttrs
<*> pAltRules

an extension with extra elements
of a earlier introduced right hand side of a production.

Extending Productions

Defining Prefixes

Adding Text to the Generated System

An Example Session.

As a first example we take the well known RepMin problem. The
input of the program is a binary tree, and it produces a binary tree
of the same shape. In the new tree however all values in the leaves
are equal to the minimum of the values in the leafs in the original
tree. We will show two versions: the full version
in which all semantic functions made explicit, and one in which
many shorthand notation is used.

The Full Version

The full code of this example is given in RepMin1.ag.
We start by defining the structure of the tree. One might look at
this both as a piece of abstract grammar, with two nonterminals, or
as two data type definitions. The difference with a Haskell data type
definition is that the fields are associated with a name, and not
only by position.

We split the computation to be performed into three different
aspects:

the computation of the minimal value

making the minimal value available at the leaves

constructing the final result

For the computation of the minimal value we introduce one
synthesised attribute m:

ATTR Tree [ | | m: Int ]

That this is an synthesised attribute follows from the fact that
the attribute specification is located after the second vertical bar
|. Next we specify the
computation of the minimum value by providing semantic rules. Such
rules may be grouped by non-terminal and by production. In the next
two rules we see the non-terminal that applies
(Tree), the alternative for
which the rule is defined (Leaf
or Bin), the element of the
alternative to which the attribute that is being defined is
associated (in this case the left hand side of the production that is
always called LHS), the
attribute that is being defined
(m), and the Haskell expression
that describes the computation . This Haskell expression is either a
complicated expression, in which case we represent is as a string
("left_m `min` right_m"), or a
simple identifier (int).

SEM Tree
| Leaf LHS .m = int
| Bin LHS .m = "left_m `min` right_m"

Next we introduce an inherited attribute
(minval) that describes how the
computed minimal value is passed from the root of the tree to the
leaves. That this is an inherited attribute stems from the fact that
its definition occurs before the first vertical bar. In the Haskell
expression we may refer to other accessible attributes by mean's of
the identifier <element
name>_<attribute name>. In order to refer to the
inherited attributes of the right hand side we prefix the name of the
attribute by lhs_.

Finally we have to tie the knot at the root of the problem: the
value that is computed as the minimum value is the value that should
be passed down the tree, and as a result we are only interested in
the constructed tree, and not in that minimum value.

In this completely equivalent program we can notice the following
abbreviations:

The field Int in the
production Leaf has not been
explicitly named (nor the field
Tree in the production
Root or nonterminal
Root). If this is the case
its name is the same as the name of the non-terminal corresponding
to that field, however with its first letter replaced by the lower
case equivalent (i.e. int
and tree).

If a child has an inherited attribute with the same name as
its father, and no semantic function has been specified, a
so-called copy rule is generated by the system that corresponds to
the identity function applied to the inherited attribute of the
father. This makes the value
minval being automatically
passed down to the leaves of the tree (this is an instance of a
more general rule for generating copy rules that will be explained
later).

If a father node has a synthesised attribute for which no rule
is given, and there is a child which also has a synthesised
attribute with the same name a copy rule is generated (this is an
instance of a more general rule for generating copy rules that
will be explained later).

Attributes may not only be defined by a special
ATTR construct, but also
after the non-terminal occurring in a
DATA section or a
SEM section.

Copy Rule Generation

In case no definition was given for a specific atribute the system
will try the following strategy in generating a definition:

If there exists a local attribute, or an elememnt in the
pattern of a local attribute that has the same name as the
attribute for which the definition is missing, a rule is geneated
through which that elemnet is referred.

If the previous rule does not solve the problem, and there
exists a filed in the production for which no attributes have been
specified, and this field has the same name as the attribute for
which the definition is missing, this filed is taken as the value
to be usedfor thi attribute.

An Example Language

We have developed a compiler for a small language in a stepwise fashion. You
can find the description of the language and the associated files at the Implementation
of Programming Languages course.

Errors

The error messages are generated in a rather generic way. We hope
they are self-explaining. Some further work needs to be done on
making the system continue once an error was reported.