.LP
.ds RF Mikhail Auguston, University of Latvia
.DA
.AI
University of Latvia
Institute of Mathematics and Computer Science
.TL
RIGAL PROGRAMMING SYSTEM
LANGUAGE DESCRIPTION
.AU
by  MIKHAIL  AUGUSTON



.nr PS 9
.nr VS 12
.SH
                         TABLE OF CONTENTS
.LP
 ABSTRACT
 1. Introduction
 2. Implementation
 3. Lexical Rules
 4.   Data
 4.1 Atoms
 4.2 Variables
 4.3 Lists
 4.4 Trees
 5. Expressions
 5.1 Accumulative Assignment Statements
 5.2  Semantics of Variables
 6. Rules
 6.1. Simple Patterns
 6.2. Assignment in Patterns
 6.3 Rule Branches. Onfail Operations
 6.4 Special Variables
 7. Compound Patterns
 7.1 List Pattern
 7.2 Iterative Pattern of Sequence
 7.3 Patterns for Alternative and Option
 7.4 Tree Pattern
 7.5 Iterative Tree Pattern
 7.6 Names of Lists and Trees in Patterns
 7.7 Patterns of Logical Condition Testing
 8. Statements
 8.1 Assignment Statement
 8.2 Conditional Statement
 8.3 FAIL Statement
 8.4 Loop Statements
 8.5 Rule Call
 9. Input and Output
 9.1 SAVE and LOAD Statements
 9.2 Text Output
 10. Program Structure
 10.1 Local Variables
 10.2 References to Variables of Other Rules
 10.3 Attribute Grammars and RIGAL. Global Attributes
 11. Built-in Rules
 12. Sample Compiler
 12.1 TOYLAN Language
 12.2 Intermediate Form of Program.
 Abstract syntax tree
 12.3 Target Language BAL
 12.4 Main Module of Compiler
 12.5 Parsing Phase
 12.6 Code Generation Phase
 13. Conclusions and Future Work
 References


                               ABSTRACT

    A new programming language for compiler writing is described.
The  main data structures are atoms, lists and trees. The control
structures  are based on advanced pattern matching. All phases of
compilation, including parsing, optimization and code generation,
can  be  programmed  in this language in short and readable form.
Sample compiler written in RIGAL is presented. 


                         1. Introduction


    Programming  language  RIGAL  is  intended  to  be a tool for
parsing(  context  checking,  diagnosing  and  neutralization  of
errors included ), for code optimization, code generation, static
analysis   of  programs,  as  well  as  for  the  programming  of
preprocessors and convertors. 
    Almost   all   the   systems   envisaged  to  solve  compiler
construction  problems  contain  means  to  describe context-free
grammar of the source language. Earlier systems, like the Floyd -
Evans  language,  [1]  present tools to work with stack, which is
used for parsing. Parsing methods for limited grammar classes are
implemented  in  the  languages  and systems of later generations
(usually LL(1) or LR(1) ). 
    Such systems as YACC (Johnson [2]), CDL-2 ( Koster [3]), SHAG
(Agamirzyan   [4])  and  many  others  make  use  of  synchronous
implementation  of  parsing  and  different  computations,  e.g.,
formation of tables, context checking, etc. Usually these actions
are  performed  by  call of semantic subroutines, written in some
universal programming language ( e.g., in Pascal or C). 
    Attribute   grammars  advanced  by  Knuth  [5]  have  greatly
influenced  development  of  systems  for  compiler construction.
Systems,  like,  SUPER  (Serebryakov  [6]), ELMA (Vooglaid, Lepp,
Lijb  [7]),  MUG2 (Wilhelm [9]) are based on the use of attribute
grammars not only for parsing, but for code generation as well. 
    Pattern  matching  is  a  convenient  tool for programming of
parsing,  optimization and code generation. The REFAL programming
language  [10], acknowledged for translator writing, may serve as
a good example. 
    Vienna method for defining semantics of programming languages
[11] suggests the usage of labelled trees in order to present the
abstract   syntax  of  programs.  Representation  of  compilation
intermediate  results  in  the  tree  form  has become usual (see
[12]). 
    Dependence  of  control  structures  in the program from data
structures used for program's work is one of the basic principles
in  programming. The recursive descent method could be considered
to be the application of dependence principle. 
    The above mentioned ideas and methods were taken into account
when creating RIGAL language. 
    The  language  possesses  few  basic notions. Data structures
contain  atoms,  lists  and  trees. Advanced mechanism of pattern
matching lies at the basis of control structures. 
    The  fact  that  RIGAL  is  a  closed  language  makes  RIGAL
distinctive.   That   means   that   almost   all  the  necessary
computations and input-output could be executed by internal means
and  there  is  no  need  to  use  external semantic subroutines.
Therefore the portability of RIGAL programs to other computers is
increased. 
    Means  for  work  with  trees,  different patterns including,
enable  both  programming  of parsing algorithms and optimization
phases  and code generation as well. The language supports design
of multipass translators. Trees are used as intermediate data. 
    The  language  allows to split the program into small modules
(rules)  and  presents  various  means  to arrange interaction of
these modules. Pattern matching is used for parameter passing. 
    RIGAL   supports   attribute   translation  scheme  and  easy
implementation   of   synthesized  and  inherited  attributes  is
possible.  The problem of global attributes is solved by usage of
special references. 
    Lexical  analysis  is  a  separate  task and requires special
language  facilities  for  description  as it is, for example, in
LEX/YACC  [2]  system. In the current implementation of RIGAL two
scanners are included that accept lexics of Pascal, C,
Modula-2  and RIGAL. 


                        2. Implementation

    RIGAL was designed and implemented in the Computing Center of
Latvia  University  in  years 1987-1988. The first implementation
was for PDP-11 in RSX-11. 
    At the present stage RIGAL interpreter has been developed and
optimizing compiler RIGAL -> Pascal has been implemented by means
of  RIGAL  itself.  The  interpreter  and  the compiler have been
ported to VAX/VMS , IBM PC XT/AT (MS-DOS and MS-Windows) and
Unix/SUN environments. 


                        3. Lexical Rules

    The  text  of  RIGAL  program is a sequence of tokens - atoms
(e.g.,  identifiers and integers ), keywords (e.g., if, RETURN ),
special  symbols  (e.g.,  +,  ##  ), names of variables and rules
(e.g.,  $A,  #L  ).  Tokens  may  be  surrounded by any number of
blanks.  A  comment is any string of symbols that begins with two
consecutive  symbols  '-' (minus ). The end of the comment is the
end of the line. For example, 
 #Sum    -- rule for addition of two numbers
      $N1   -- the first number
      $N2   -- the second number
    /  RETURN  $N1 + $N2 /   -- return of the result
 ##

                            4.   Data

                            4.1 Atoms

    An  atom is a string of symbols. If the atom is an identifier
(  the  first symbol is a letter followed by letters or digits or
underscore  symbols),  in  the  text of RIGAL program it could be
written directly: AABC total_number x25 
    Numerical atoms are integers, for instance, 2, 187, 0, -25
    In other cases the atom is quoted: '+'    ':='     '1st'
    Some  identifiers  are reserved as keywords in RIGAL. If they
are  used  as  RIGAL  atoms,  they should be quoted. For example,
'IF',  'RETURN'.  Besides, any atom, which is an identifier, also
can be quoted - ABC and 'ABC' represent one and the same atom. 
    It  should be noted that 25 and '25' are different atoms, the
latter is just a string of symbols '2' and '5'. 
    Two special atoms are distinguished in the language.
    NULL  -  this  atom  is  frequently  yielded  as  a result of
different  operations,  if something was incorrect in the process
of  computations.  This  atom  also  represents an empty list, an
empty tree and Boolean value "false". 
     T  -  usually this atom is yielded by logical operations and
represents value "true". 

                          4.2 Variables

    The name of a variable must begin with the symbol $, followed
by  an  identifier.  Value  can  be  assigned  to a variable, for
example, by the help of assignment statement: $E := A 
    In this case the atom A becomes value of the variable $E.
    In  RIGAL variables have no types, the same variable may have
an atom, a list or a tree as a value in different time moments. 

                            4.3 Lists

    Ordered sequences, i.e., lists can be composed from atoms and
from  other  lists  and trees, as well. A special function - list
constructor  serves for list formation. For instance, (. A B C .)
forms a list of three atoms A, B and C. 
    Arguments  of  the  list  constructor may be expressions. The
sample  $E  := (. (. 8 14 7 .) (. A B .) .) could be rewritten as
follows: 
  $A := (. 8  14  7 .); $B := (. A  B  .); $E := (. $A  $B .);
    Separate  elements  of  the list can be selected by indexing.
Hence, $B [1] is atom A, $A [2] is atom 14, $E [2] is list 
 (. A B .), but $E [10] is atom NULL
    If  the value of the index is a negative number, for instance
-N, then the N-th element, beginning from the end of the list, is
selected. For example, $A [-1] is atom 7. 
    The  necessity  to  add one more element to the list is quite
common. Operation !. is envisaged for this purpose. 
    Example. (. A  B .) !. C  yields the list (. A  B  C .)
    To link two lists in a new list the operation !! is applied (
list  concatenation). For instance, (. A B .) !! (. C D .) yields
(. A B C D .). 

                            4.4 Trees

    Tree constructor is used to create a tree. For example,
                      <.  A : B,  C : D  .>
    One  can  imagine  tree  as  a graph, the nodes and arches of
which are marked by some objects. 
    Objects   before  ':'  in  the  tree  constructor  are  named
selectors.  In  the  given implementation solely atoms, which are
identifiers  (  except  NULL  ),  may  serve as selectors. In the
graphical  representation  selectors  correspond to arches of the
graph.  All  selectors of one and the same level in the tree must
be different. 
    Any  object  -  atom,  list  or  tree,  except atom NULL, may
correspond  to  terminal  nodes  of  the  graph ( "leaves" of the
tree). Hence, multilayer trees can be built. For instance, 
              <. A : B,  C : <. D : E, F : G .> .>
    Pair  "selector  : object" in the tree is named branch of the
tree. Branches are unordered in the tree. 
    Likewise  for  the list constructor, the tree constructor may
be  described  by  expressions  (  in  both  selector  and object
places),  for instance, 
    $X := D; $B := (. 2 8 .); 
    $C := <. A : <. M : K .>, $X : '+' , E : $B .>; 
    Select  operation serves to extract the tree component. It is
in the following form: tree . sel , where tree is the expression,
whose  value  must be some tree, but sel is the expression, whose
value must be an atom-identifier. 
    Consequently,  
    $C . A is the tree <. M : K .> , 
    $C . D is the
atom '+' , 
    $C . E is the list (. 2 8 .) , 
    $C . E[2] is the atom 8
, $C . A . M is the atom K 
    If there is no branch with a given selector in the tree, then
the result is NULL: $C . W is atom NULL. 
    Operation  of the tree "addition" is performed as well: T1 ++
T2 , where T1 and T2 are trees. Tree T2 branches are added to the
tree  T1  one  by  one.  If in the tree T1 there already exists a
branch with the same selector, the branch is substituted by a new
one. Therefore, the operation "++" is not commutative. 
    It  should  be  pointed  out  that  the  tree  constructor is
computed  from  left to right, i.e., 
  <. s1 : a1, s2 : a2, s3 : a3.> 
 gives the same result as the expression 
  (( NULL ++ <. s1 : a1 .> ) ++ <. s2 : a2 .>) ++ <. s3 : a3 .>

                         5. Expressions

    Operations  = and <> serve for the comparison of objects. The
result of the comparison is either T ("true") or NULL ("false"). 
    Atoms  are  matched directly, for instance, a = b gives NULL,
25 = 25 gives T, 17 <> 25 gives T. 
    Lists  are  considered equal iff they contain equal number of
components and if these components are equal respectively. 
    Trees  are  considered equal iff they contain equal number of
branches  and  if  one of the trees contains the branch "S : OB",
then  the  other tree also contains the branch "S : OB1" and OB =
OB1. 
    Arithmetical  operations  +,  -, *, DIV, MOD are assigned for
numerical  atoms.  The  essence of these operations is similar to
those  in  Pascal.  The  result of an arithmetical operation is a
numerical  atom.  Atom  NULL  is also admitted as the argument of
arithmetical  operation, in this case integer 0 is supposed to be
its  value.  Under matching these atoms are considered different,
i.e., NULL = 0 gives NULL. 
    Besides  the  operations  =  and <> numerical values could be
compared by the help of >, <, >= and <=. 
    Logical  operations AND, OR and NOT usually are applied under
conditions  in  conditional  statements.  Their  arguments may be
arbitrary  objects.  If  the  object  differs  from  NULL,  it is
supposed  to  have  the value "true" in a logical operation. Atom
NULL  represents  the  "false"  value.  The  result  of a logical
operation always is either T or NULL. 
    In order to make complex hierarchic objects ( trees and lists
of  complex  structure  )  more visual and to improve the work of
pattern  matching,  trees and lists may be labelled. A name is an
atom  opposed  to  the  root  node of the tree or the whole list.
Labelling operation is written the following way: 
                             A  ::  OB 
 where A is an atom , OB is
an object (a tree or a list). For example, 
        Add_op :: <. arg1 : 5, arg2 : 4 .> 
    The  execution  order  of  operations  in  the  expression is
controlled  by  parentheses  "(" and ")". Priority is assigned to
every  operation,  and,  if the execution order is not defined by
parentheses,   operations   are   executed   according  to  their
priorities  -  beginning  with  the  higher and proceeding to the
lower. 
    Operations are listed in decreasing order of priorities (some
operations will be discussed later). 
  1) Rule call, list constructor, tree constructor, last
  2) Selector ".", index "[]",  ::
  3) NOT, unary -
  4) *, DIV, MOD
  5) !. , !! , ++ , +, binary -
  6) = , <> , > , < , >= , <=
  7) AND
  8) OR
    Binary operations of the same priority are executed from left
to right, while unary operations from right to left. 


             5.1 Accumulative Assignment Statements

    It is quite often that working with a list or a tree elements
are  added  step  by step, thus the growing object is retained in
the  same  variable. Therefore short form of assignment statement
has  been  introduced.  For  the  operations  !.  , !! , ++ and +
statements of the form $X := $X op Expr can be written as 
 $X op := Expr .


                   5.2  Semantics of Variables

    In  the  implementation of RIGAL every object - atom, list or
tree  has  a  descriptor.  It  is  a  special data structure that
contains  some  information about this object: the value of atom,
the  number  of  elements  and pointers to elements for lists and
trees. Variables have pointers to object descriptors as values. 
    Statement  $X  :=  OB assigns to the variable $X a pointer to
the descriptor of object OB. After the execution of the statement
$Y  :=  $X  both variables $X and $Y contain pointers to the same
object OB. 
    Operations  !.:=  , !!:= , +:= and ++:= change the descriptor
of  their  first argument, i.e., have a side effect. Sometimes it
can be undesirable. For example, 
            $A := (. 3 17 .); $B := $A;  $B  !.:= 25;
    The  value  of  $B  becomes list (. 3 17 25 .), but operation
!.:= has added element 25 immediately to the list (. 3 17 .), and
the  descriptor  of  this  list  is  changed. As the value of the
variable  $A  was  a  pointer  to this descriptor, then after the
execution  of  the statement $B !.:= 25 the value of the variable
$A is changed, too. 
    To  prevent this, we must have a copy of the object, to which
$A refers to before assigning it to $B. Operation  COPY( OB )
is  used  for  this purpose. It makes a copy of the descriptor of
the object OB. 
    Now  we can write a "safe" call of the operation !.:= in such
a way: $A := (. 3 17 .); $B := COPY( $A); $B !.:= 25; 
    As  a result $B takes the value (. 3 17 25 .), but $A remains
equal  to  (.  3  17  .).  The  same effect can be obtained after
execution of statements $A := (. 3 17 .); $B := $A !. 25; 


                            6. Rules

    The concept of rule is analogous to concepts of procedure and
function in conventional languages, such as Pascal or C. 
    First  of all, by the help of a rule we can check, whether an
object  or  a sequence of objects complies with some grammar. For
this  purpose  rule  has  a pattern. Objects, assigned under rule
call  (arguments  of  the rule), are matched with the pattern. If
there  is  a  necessity,  some  operations could be executed, for
instance,     computations     and    input-output    operations,
simultaneously with rule arguments and pattern matching. 
    Rule  that  is  called can compute and return back some value
(object), i.e., it can be used as function. 
    Depending  on  the  result  of  rule  arguments  and  pattern
matching,  the  rule  call ends with success or failure. Thus the
rule call could be used in another rule patterns. 


                      6.1. Simple Patterns

    Definition of the rule begins with the indication of the name
of   the   rule   in   the  form  of  #LLL  ,  where  LLL  is  an
atom-identifier. In the most common case the pattern is indicated
after  the  name  of  the  rule. Rule definition ends with symbol
'##'. For instance, 
 #L1    A  B   ##
    In  this  case the pattern consists of atoms A and B. Call of
the rule #L1 takes place, for instance, the following way : 
                          #L1 ( A  B )
    The  sequence of objects - atoms A and B, is assigned as rule
arguments. 
    After rule call the argument and pattern matching begins. The
first  argument - atom A is matched with the first pattern, which
is  also  an  atom.  These  atoms are equal, so their matching is
successful.  After  that  the  next  object  from the sequence of
arguments  -  atom  B  and  the  next  pattern,  also atom B, are
matched.  Their  matching is also successful, therefore, the call
of the rule #L1 is successful, too. 
    #L1(  A  C ) call fails, because the second argument - atom C
was not successfully matched with the pattern - atom B. 
    #L1(  A  )  call  fails, as there is no object for the second
pattern with which it could be matched successfully. 
    But  #L1(  A  B  C)  call  is  successful,  as the third rule
argument - atom C was not demanded by any pattern and matching of
the first two rule arguments was successful. 
    Arbitrary  atoms,  both  non-numerical  and numerical, may be
described as patterns. 
    Such  operations as assignment statements, input - output and
others  may be indicated before the pattern, after it and between
patterns.  These statements are quoted in the pair of symbols '/'
.  If there is a necessity to write several statements within the
pair '/' , these statements are separated by the symbol ';' . 
    A  group  of  statements  is executed in the rule pattern, if
matching  of  the  previous  pattern  with the corresponding rule
arguments was successful. 
    The  value  returned  by  the rule is worked out by statement
RETURN   .   It   has  the  following  form:  RETURN  expression.
Simultaneously  this  statement  completes  the  execution of the
rule. 

    Example.
 #L2  'begin'  'end'  / RETURN  'The pair begin-end' /  ##

    Rule call is illustrated in the following example.
 $A   :=   #L2   (   'begin'  'end'  );  
    As  a  result  the  atom
'Thetpairtbegin-end' is assigned to the variable $A. 
    If  RETURN  statement  is not described in the rule, then the
NULL value is returned. 
    If  the rule call ends in failure, then usually value NULL is
returned, although in case of failure, it is possible to work out
the returned value, which is not NULL; for this purpose statement
RETURN must be used in onfail-operations (see sect.6.3.). 
    Variable  could  be  used  as pattern. It is matched with one
object  (atom, list or tree) from the sequence of rule arguments.
Matching  always  ends  in  success,  and  as  a  side effect the
variable  obtains  this object as value. For example, 
   #L3 $A $B /RETURN (. $B $A .)/ ## 
    After  the  call  $X := #L3 ( 1 2) the variable $X obtains as
value (. 2 1 .) 
    The  rule  pattern,  in  its  turn,  may  refer  to some rule
(recursively,  as well). Then the subsequence of the calling rule
arguments  become arguments of rule - pattern. If the call of the
rule  -  pattern is successful, then matching of further patterns
of the calling rule with the remaining arguments proceeds. If the
call  of  the  rule - pattern fails, then the pattern matching of
the calling rule fails, too. For example, 
 #L4   A  #L5  D  ##
 #L5   B  C       ##
    Then the call #L4( A B C D) is successful, but the call
 #L4( A E F D) is unsuccessful.
    There  is  a  number  of  built-in rules in the language (see
section  11.). For instance, #NUMBER is successfully matched with
a  numerical atom and returns it as a value, other arguments fail
and  return  NULL.  #IDENT  is  successfully  matched with atom -
identifier and returns it as value. 


                   6.2. Assignment in Patterns

    Every   pattern,   when   successfully   matched   with   the
corresponding  rule  argument,  returns  some value. The value of
atom  pattern  coincides  with  this  atom, the value of variable
pattern  coincides  with the value obtained by this variable as a
result  of matching with the arguments. The value of rule pattern
is defined by statement RETURN in this rule. 
    If  the matching ends in failure, the pattern usually returns
value NULL. 
    These  values,  returned by patterns, can be assigned at once
to  some variable. It is enough to write the name of the variable
and the assignment symbol ':=' before the pattern element. 
 Example.
 #L6   $A  $R := #L7  / RETURN  (. $A .) !! $R /  ##
 #L7   $B  $C   / RETURN  (.  $B  $C .) /   ##
    After execution of the statement $X := #L6 ( 1 2 3) the value
of $X will be (. 1 2 3 .) 
    Symbols  of  accumulative assignment '!.:=' , '!!:=' , '++:='
and '+:=' can be used instead of ':=' in patterns . 
    Therefore,  we can rewrite the previous example the following
way: 
 #L6_1  $R !.:= $A    $R !!:= #L7_1  / RETURN  $R /  ##
 #L7_1  $M !.:=  $B   $M !.:=  $C    / RETURN  $M /  ##
    It  should  be  noted  that  all  variables  in  the rule are
initialized by value NULL, so the value of the expression NULL !.
$A  that equals to (. $A .) is assigned to the variable $R by the
first application of the pattern $R !.:= $A in #L6_1. 
    Patterns  of the type $N := #NUMBER or $ID := #IDENT are used
very  often,  therefore, following defaults are introduced in the
language.  If  the  first  letter of the variable name is N, then
this  variable,  having been used as pattern element, will have a
successful  matching  only  with a numerical atom, in other cases
matching ends in failure, and variable obtains value NULL. If the
first  letter  of the name of the variable is I, this variable is
matched successfully only with an atom-identifier. 


              6.3 Rule Branches. Onfail Operations

    Several groups of patterns may be united in one rule.
    The  first group of patterns is applied to the rule arguments
first.  If  matching of this group of patterns with the arguments
is  successful,  the rule call is successful. But if matching has
failed, transition to the next group of patterns takes place, and
it  is  matched  with  the  same arguments. It goes on until some
group   of   rule   patterns   is   matched  with  the  arguments
successfully.  If not a single pattern group matches successfully
with rule arguments, the rule call ends in a failure. 
    Such  alternative  pattern  groups  are called rule branches,
and,  when  writing  the  rule,  they are separated by the symbol
';;'. 
    If  the  branch  fails,  the  execution  of  its patterns and
statements  is  abandoned  at  the  place,  where  branch pattern
failed,  and  control is transferred to the next branch (if there
is  one)  or  the  whole rule fails (if the current branch is the
last  one  in  the rule). Still there is a possibility to execute
some operations before exit from the branch. 
    ONFAIL  operation is a sequence of statements, written at the
end  of  the  branch  and  delimited  from  patterns  and  branch
statements by keyword ONFAIL. 
    If  ONFAIL-statements  are  described  in the branch, then in
case  of  branch  failure,  control  is  transferred  to them and
statements,  given  in ONFAIL-unit , are executed. So, in case of
branch failure, causes of failure can be analyzed and message can
be output. Statement RETURN can be executed in ONFAIL operations,
as  well. Then exit from the rule takes place (with failure), and
some other value than NULL can be returned. 


                      6.4 Special Variables

    Special  variable  without  name  $  denotes  the  first rule
argument matched by a rule pattern. 
    The  value  of  special  variable  $$  equals to the value of
current rule argument, to which current rule pattern is applied. 


                      7. Compound Patterns

    Lists,  sequences  of  elements  in  lists  and  trees can be
analyzed   by   patterns.  Nesting  of  patterns  practically  is
unlimited.  It  is allowed to insert statements before, after and
within any pattern. 

                        7.1 List Pattern

    List pattern is written in the rule the following way:
                    (.  S1 S2 ... SN .)
  where S1, S2, ..., SN are patterns . For instance, 
                  #L8  (. $E1  $E2 .)  ##
    Pattern  of  the  rule  #L8  is matched successfully with any
list, containing precisely two elements. Such call is successful: 
   #L8( (. (. 1  2 .)  <. A : B .> .) )
    But  the following calls end in failure. 
   #L8( A B ) - because
pattern will be applied to the first argument, i.e., to atom A, 
   #L8(  (. 13 .) )  -  because the argument is one element list.
    In  case of success the list pattern yields value that can be
assigned  to  some  variable. This value coincides with the whole
list, to which the pattern was applied. 


                7.2 Iterative Pattern of Sequence

    In  RIGAL  the  following  pattern  is  defined  for sequence
recognition:  (*  S1 S2 ... SN*) , where S1 , S2, ... SN are some
patterns.  This  pattern  describes  the  repetition  of enclosed
sequence of patterns zero or several times. 
    Rules  with  a variable number of arguments can be defined by
iterative pattern of sequence. For example, 
 #Sum  (*   $S +:= $N   *)  / RETURN $S /  ##
    This rule is used for summing up any amount of numbers.
 #Sum( 2  5  11) = 18  and  #Sum( 3 ) = 3
    Iterative pattern is very often used within list pattern. For
instance, the following rule counts the number of list elements. 
 #Length (. / $L := 0 / (*  $E / $L +:= 1 / *) .) / RETURN $L / ##
    Samples of rule call.
 #Length( (. A  B  C .) ) = 3  and  #Length( NULL ) = 0
    Iterative  pattern  (+  S1  S2  ... SN +) is analogous to the
pattern (* ... *), but assigns the repetition of enclosed pattern
sequence one or several times. 
    In the iterative pattern of sequence the element delimiter is
indicated  in  the form of (* S1 S2 ... SN * Delimiter ) or (+ S1
S2 ... SN + Delimiter +) 
    Atom or name of the rule may serve as Delimiter .
    Example.  Analysis  of  a  simple  Algol-like  declaration. A
fragment  of variable table coded in a tree form is returned as a
result.
 
    #Declaration $Type := ( integer ! real ) 
               (+ $Id  / $Rez ++:= <. $Id : $Type .> /  + ',')
        / RETURN $Rez /   ##

    Call #Declaration ( real X ',' Y ) returns value
                    <. X : real, Y  : real .>

    It  should  be  noted  that the pattern (* $E * ',' ) differs
from  the  pattern (* $E ',' *) in the point that the presence of
atom  ','  is  obligatory  in  the  second  pattern at the end of
sequence. 



             7.3 Patterns for Alternative and Option

    The  choice  of  several  possible  patterns  is  written the
following way: ( S1 ! S2 ! ... ! SN ) 
    Patterns  S1 , S2 , ... , SN are applied one by one from left
to right, until one of them succeeds. 
    In  case  of  success  alternative  pattern  yields value. It
coincides  with  the  value  of the successful pattern within the
alternative and may be assigned by some variable. 
    Example.   Simple   arithmetic   expression   parsing.   When
successful, an expression tree is returned, which can be regarded
as an intermediate form for the next compilation phases. 
 #Expression     $A1 := #Term
        (* $Op := ( '+' ! '-' )  $A2 := #Term
           / $A1 := <. op : $Op , arg1 : $A1 , arg2 : $A2 .> / *)
        / RETURN $A1 /   ##
 #Term   $A := ( $Id  ! $Num ) / RETURN $A /  ;;
        '('  $A := #Expression ')'  / RETURN $A /   ##

    The call #Expression( X '-' Y '+' 7 ) returns the value
  <. op: '+', arg1: <. op: '-', arg1: X, arg2: Y .>, arg2: 7 .>
    In  RIGAL  we may write a rule that matches successfully with
an empty sequence of arguments:
    #empty ## 
    Now the pattern for option can be written down: ( S ! #empty )
    In  short  form  this  issue may be written down in RIGAL the
following way: [ S ] where S is some pattern or pattern sequence.
Pattern [ S ] always ends in success. 

                        7.4 Tree Pattern

    Tree  pattern checks, whether the object is a tree with fixed
structure.  By  means of this pattern access to the components of
the tree is obtained. The tree pattern is described the following
way: 
       <.  Sel1  :  Pat1, Sel2 : Pat2, ... , SelN : PatN .>
  where
Sel1,  Sel2,  ... SelN are atoms-identifiers, but Pat1, Pat2, ...
PatN are patterns. 
    If the object, to which the tree pattern is applied, is not a
tree, then the application of the pattern fails at once. If there
is  the  selector  Sel1  in  the  tree,  then the pattern Pat1 is
applied to the corresponding object. If there is no selector Sel1
in  the  tree  or  the  application of Pat1 has failed, the whole
pattern also fails. 
    If  matching the first branch was successful, branch matching
of the pattern 'Sel2 : Pat2' , etc. begins. 
    Hence,  pattern  branches are applied to the tree in the same
order as they are written in the pattern. Therefore, the order of
tree  traversing  may  be  controlled.  It  is  possible  to have
reiterative  visit  of  branches  (  if  selectors are repeatedly
described  in  the  tree  pattern)  or  omission  of branches (if
corresponding selectors are not given in the pattern). 
    In  case of success the tree pattern returns the value, which
coincides  with  the  whole object - a tree, to which the pattern
was  applied,  irrespective  of presence of all tree selectors in
the pattern or absence of some. 
    Example.  Let us suppose expression tree to be formed like in
the  above example. The task is to traverse the tree and return a
list that represents the Polish postfix form of this expression. 
 #Postfix_form  <. arg1: $Rez := #Postfix_form,
                  arg2: $Rez !!:= #Postfix_form,
                  op:   $Rez !.:= $Op  .>   / RETURN $Rez /  ;;
               $Rez := ( $Id ! $Num )  / RETURN (. $Rez .) /  ##
    The  call 
    #Postfix_form(  <.  op: '-', 
                        arg1: X,
                        arg2: <. op:'+',  
                                 arg1:  Y, 
                                 arg2: 5 .> .>) 
 returns the value 
    (. X Y 5 '+' '-'.) 

    Some  branches  in  the  tree  pattern  may  be  described as
optional,  in this case they are enclosed in brackets '[' and ']'
.  If  there  is  no  selector of optional branch in the argument
tree,  its  pattern  is  omitted  and  transition to next pattern
branch  takes  place.  If  there is a selector of the type in the
argument tree, the pattern branch is developed as usual. 


                   7.5 Iterative Tree Pattern

    The simplest form of iterative tree pattern is the following:
                        <*  $Var  :  P  *>
  where  $Var  is  some
variable, and P is a pattern. 
    A  loop  over  the  tree  is  performed  by  the help of this
pattern.  All  selectors of the argument tree are assigned to the
variable  $Var  one  by  one.  The  pattern  P is applied to each
object,  which  corresponds  in  the argument tree to the current
selector  in  the  variable  $Var. If even one application of the
pattern  P  fails,  the  whole  iterative tree pattern fails. For
example, 
 #Variable_table  <* $Id : $E := ( integer ! real )
                   / $R !.:= (. $Id  $E .)/  *>  / RETURN $R /  ##
 Call example.
 #Variable_table( <. X : integer, Y : real, Z : real .> ) =
    (. (. X  integer .) (. Y  real .) (. Z  real .)  .)
    Sometimes,  performing  a  loop  over the tree, some branches
should  be  updated  in a special way. For this purpose iterative
tree pattern with distinguished branches is used. 
   <*  Sel1  :  Pat1,  Sel2 : Pat2, ..., SelN : PatN, $Var : P *>
 where Sel1, Sel2, ... SelN are atoms-identifiers;
       Pat1, Pat2, ... PatN  are  patterns,
 i.e., like elements in simple tree pattern;
$Var  is  a  variable,  but  P is a pattern, as in simple case of
iterative tree pattern. 
    The  pattern  is  applied  to the argument tree the following
way.  First of all distinguished pattern branches 'Sel : Pat' are
developed.  Their  matching  with  branches  of the argument tree
happens  exactly  the  same way as with simple tree pattern. Then
the  element  '$Var  :  P'  is  applied  to other branches of the
argument tree the same way as in simple iterative tree pattern. 
    Some distinguished branches can be optional, for this purpose
they  are enclosed in brackets '[' and ']'. Semantics is the same
as in the case of simple tree pattern. 
    Example.  Let  it be a tree of arbitrary size. In some of its
subtrees  there is the selector LABEL, to which numerical atom is
attached. All these numbers over the whole tree must be collected
in a list, and the list must be returned as result. #Label_list 
 <*  [ LABEL : $Result !.:= $N ],
        $S   : $Result !!:= #Label_list  *> / RETURN  $Result / ;;
    $E            ##
    The  rule  has  two  branches. Traversing of the tree and its
subtrees  is described in the first branch. The resulting list is
formed  in  the  variable  $Result. The traversing of subtrees is
carried   out   by  the  help  of  recursive  call  of  the  rule
#Label_list. The second branch of the rule consisting of just one
pattern  $E  is  applied  at  the leaves of the tree. The pattern
matches successfully with any object and the whole branch returns
value NULL, which is accepted as empty list at the previous level
of recursion. 
    Call  sample. 
    #Label_list  (  <.  A: <. LABEL: 5, B: abc .>,
                        LABEL: 17, D: 25 .> ) = (. 17 5 .) 


            7.6 Names of Lists and Trees in Patterns

    The assignment of names was discussed in Section 5.
    In  list  and  tree patterns we can have matching of list and
tree names with the described values or simply get these names in
the  described  variable.  For  this  purpose  the  name  may  be
indicated before list or tree pattern: atom :: pattern 
    If  the atom described in the pattern coincides with the name
of  the  list  (or  tree),  to  which the pattern is applied, the
application  of  the  pattern  to the argument begins. If it does
not,  other  pattern  elements  are  not  applied and the pattern
fails. 
    To obtain access to the name of the argument, instead of atom
we  must  indicate the variable, in which the name is assigned as
value. 


            7.7 Patterns of Logical Condition Testing

    Pattern  of  the  type: S' ( expression ) works the following
way.  First  of  all  the  expression  is evaluated. If its value
differs  from  NULL,  the pattern is successful. The value of the
matched  argument  is  returned  as  the value of the pattern, if
matching was successful. If the value of the expression equals to
NULL, the pattern fails and returns NULL. 
    The  value  of  special  variable  $$  in  the  expression of
S-pattern equals to the value of the argument, to which S-pattern
is applied. 
    The  skip  of  token sequence until the nearest symbol ';' is
described by the pattern: (* S' ( $$ <> ';' ) *) 
    Let  under  parsing a case is accentuated when the assignment
statement  is  in the form of X := X + E , where X is a variable,
and  E  is an expression. This case could be described by pattern
of the type: $Id ':=' S' ( $$ = $Id ) '+' #Expression 
    Pattern  of  the  type:  V'  (  expression ) works similar to
S-pattern, yet in case of success no advancing along the sequence
of  rule  arguments  takes  place, so the next pattern element is
applied to the same argument. 
    This  pattern is useful for context condition check. Example.
The pattern
     S' ( #NUMBER($$) and ( $$ > 7))
  may be substituted by
a sequence of patterns 
     $Num V'($Num > 7) 


                         8. Statements

                    8.1 Assignment Statement

    In  the  left  side of assignment statement a variable may be
indicated,  which  is  followed  by  an  arbitrary number of list
indexes and/or tree selectors. For example, 
  $X := (. A B C .); $Y:= <. D : E, F : G .>; 
    After assignment $X[2] := T  the value of $X is (. A T C .) .
    After assignment $Y.D :=17 the value of $Y is <.D :17, F:G .>
    The  execution of the statement $Y.A := T yields the run time
error  message.  The  necessary  result is obtained the following
way: 
   $Y ++:= <. A : T .>;
    Tree  branch  is  deleted by assigning an empty object to the
corresponding selector: $Y.D := NULL; 

                  8.2 Conditional Statement

   Conditional statement has the following form:
                 IF  expression  ->  statements 
 Then branches may
follow (it is not compulsory) 
                ELSIF   expression   ->   statements  
 Conditional
statement ends with keyword FI. 
    In  conditional  statement  branches expressions are computed
one  by  one, until a value different from NULL is obtained. Then
the statements described in this branch are executed. 


                       8.3 FAIL Statement

    FAIL statement finishes the execution of the rule branch with
failure.  Example.  In order to repair errors in parsing process,
the  sequence  of  tokens should be skipped quite frequently, for
instance, until semicolon symbol. It is done the following way. 
 #statement    ...            ;; -- branches for statement analysis
    (*  #Not_semicolon   *)   ';'   -- no statement is recognised
 ##
 #Not_semicolon   $E / IF  $E = ';' -> FAIL  FI/   ##


                      8.4 Loop Statements

    Statement of the types
         FORALL $VAR  IN expression
            DO  statements  OD
         FORALL SELECTORS $VAR BRANCHES $VAR1 IN expression
            DO  statements  OD
         FORALL BRANCHES $VAR1 IN expression
            DO statements OD
  loops over a list or a tree. 
    The  value of the expression must be either a list or a tree.
Value  of the current list element (if the loop is over the list)
or  value  of the current selector (if the loop is over the tree)
is   assigned   to  the  loop  variable  $VAR  one  by  one.  The
corresponding  branch value is assigned to the variable $VAR1 (if
the  loop  is  over the tree). Statements, describing body of the
loop, may use the current value of the variables $VAR and $VAR1. 

    Loop statement of the type
                     LOOP  statements  END;
 repeats statements of
the  loop  body,  until  one of the statements - BREAK, RETURN or
FAIL is not executed. 

                          8.5 Rule Call

    If  a  rule is called just to execute statements described in
it,  and  value  returned  by the rule is not necessary, the rule
call  is  written down as statement. It is analogous to procedure
call in traditional programming languages. Success/failure of the
rule and value returned by it is disregarded in such a call. 


                       9. Input and Output

                 9.1 SAVE and LOAD Statements

    Objects created by RIGAL program (atoms, lists, trees) can be
saved in the file and loaded back to the memory. 
    Statement
  SAVE  $Var  file-specification
  unloads the object,
which  is  the  value  of  the variable $Var to the file with the
given  specification.  File,  formed  by SAVE statement, contains
precisely one object (atom, list or tree). 
    We  can  load  the  object from the file in the memory having
executed statement:
  LOAD $Var file-specification 


                         9.2 Text Output

    To  output  texts  (messages,  generated object codes, etc. )
several  text  files can be opened in the RIGAL program. The text
file  FFF  is  opened  by  statement: OPEN FFF file-specification
File-specification  may be an expression. It presents the name of
the file on the device. 
    Statement of the type
                FFF  <<  Expr1 Expr2 ... ExprN 
 outputs a sequence
of atoms to the file FFF. Values of expressions Expr1, Expr2, ...
are  either  atoms or lists consisting of objects, different from
trees. 
    This  statement  outputs atoms as sequences of symbols to the
text  file, inserting a blank after every atom. Atoms in the list
are output in the same order as they are in the list. 
    Example.   FFF <<  A B 12 ;
    A  string  of  characters  is output in the text file FFF the
following way: "A B 12 " 
    Symbol  @  in the sequence of expressions of output statement
switches over to other output mode of blanks separating atoms. By
default  at  the  beginning of execution of text output statement
the output mode with blanks is on. 
    Example.   FFF << A B  @  C D  25  @  E F 57 ;
    The following string of characters is output to the text file
                      "A B CD25E F      57"
    Statement  of  the  type 
                   FFF << ... 
 always begins output with
the  beginning  of a new record. Output statement of the type
                    FFF <] ... 
continues output in the current record. 

    By the help of the statement of the type

                        PRINT  expression
 the value of atom, list
or  tree can be output in a readable form on the display, disk or
on the printer. It is useful when debugging RIGAL programs. 


                      10. Program Structure

    Program  written  in  the RIGAL language consists of the main
program and rules. The main program text must be at the beginning
of  the  RIGAL program text, but the text of the rules is written
afterwards.  Main  program,  as  well as rule, begins by the name
indication in the form of #main_program_name, then statements are
described, separated by symbols ';' . The end of the main program
is marked by the symbol '##'. 
    Usually operations that deal with the initial object loading,
rule  call  and  unloading  of  created  objects in the files are
concentrated  in the main program. Therefore, main program has no
pattern elements or arguments of its own. 
    When  RIGAL  is  used  for  parsing, text file with the input
information first is updated by scanner, which transforms it into
a  list  of tokens. Thus rules describing required parsing can be
applied  to this list. Intermediate results of the RIGAL program,
for  instance,  abstract  syntax trees obtained by parsing can be
unloaded  in  the  file  by  the help of SAVE statement. They are
unloaded  so,  that  other  RIGAL  programs (for instance, those,
which  implement  phase of code generation in compilers) can load
them  as  input  data. Sample of main program is given in Section
12.4. 
    Rules can be written down after main program  in  any  order.


                      10.1 Local Variables

    There are no special variable declarations in RIGAL. The fact
that  a variable is used in some statement, pattern or expression
implies  that  the  variable  is defined as local variable of the
rule or the main program. 
    For recursive rule calls local rule variables are pushed in a
stack,  so,  that  every  active rule instance has its own set of
local variables. 
    All  variables  are  initialized  by the NULL value, when the
corresponding  rule  or  the  main program is called and when the
execution of rule branch starts. 

           10.2 References to Variables of Other Rules

    The  construction of RIGAL makes it possible to obtain access
from  the  rule  to  local  variables of another rule. It has the
following form: LAST #L $X 
    This  reference  denotes  the value of the variable $X in the
last  (in  time  ) and still active instance of the rule #L. Such
references  can  be  used  in  left and right sides of assignment
statement. 
    If  at  the moment of evaluation of the expression LAST #L $X
there  is  no  active  instance  of the rule #L, the value of the
expression  LAST  #L $X equals NULL. If such LAST #L $X is in the
left  side  of assignment statement, the statement is disregarded
(and run time error message is output). 
    By  the  help  of  LAST  we  may  refer to both rule and main
program variables. 

      10.3 Attribute Grammars and RIGAL. Global Attributes

    There  is a close analogy between attribute grammar and RIGAL
program.  Rules  in RIGAL correspond to grammar nonterminals, and
variables - to attributes. 
    In  attribute  grammars  the  greatest part of attributes are
used  as  transit attributes. To avoid this global attributes are
introduced in the attribute grammar implementations. The usage of
LAST references solves this problem in RIGAL. 
    Implementation  of  both synthesized and inherited attributes
is possible as it is demonstrated in the following scheme. 
 #LA  ... ... ...
    assigns value to the attribute $A
    calls #LB     ... ... ...     ##
 #LB ... ... ...
    $B1 := LAST #LA $A -- uses the inherited attribute $A from #LA
    calls #LC
     -- after this call the  value  of  the attribute  $C
     -- from #LC is  assigned to the synthesized attribute $B2
    ... ... ...     ##
 #LC ... ... ...
    assigns value to the attribute $C
    LAST #LB $B2 := $C  -- the value is assigned to  the
                         -- synthesized  attribute  $B2 of #LB
    ... ... ...         ##


                       11. Built-in Rules

    There  is  a  number of built-in rules in the language. These
rules   implement  functions,  the  implementation  of  which  is
impossible or ineffective by other language means. 
    Call  of  built-in rules is written down the same way as call
of  rules  defined  by  the  user  itself. Along with the value a
built-in rule yields success or failure hence, built-in rules are
used as patterns. 
    There are predicates such as #ATOM(E), #NUMBER(E), #IDENT(E),
#LIST(E) and #TREE(E). 
    The built-in rule #LEN(E) returns numerical atom as value. If
E  is an atom, then for non-numerical atoms it returns the number
of atom symbols. For numerical atoms this rule returns the number
of  significant  digits plus 1, if the atom is a negative number.
#LEN( NULL) equals 0, #LEN('ABC') equals 3, #LEN(-185) equals 4. 
    If  E  is  a  list,  then  #LEN(E) returns the number of list
elements, but, if E is a tree, then it returns the number of tree
branches. 
    #EXPLODE(E).  If  E  is an atom, then it succeeds and returns
one  character atom list that represents the value E 'decomposed'
in   separate   characters.  If  E  is  a  numerical  atom,  only
significant digits are present. 
    Examples.#EXPLODE(X25) yields (. 'X' '2' '5' .).
              #EXPLODE(-34) yields (. '-' '3' '4' .).
    #IMPLODE(E1 E2 ... EN). This rule yields the concatenation of
atoms or lists E1, E2, ..., EN in a new, non-numerical atom. 
    Examples.#IMPLODE( A B 34) equals 'AB34'.
              #IMPLODE(25 (. A -3 .) ) equals '25A-3'.
    #CHR(N). The rule returns an atom, which consists of just one
ASCII character with the code N ( 0 <= N <= 127). 
    #ORD(A). Returns an integer, which is an internal code of the
first character of the nonnumerical atom A. 
    For instance, #ORD( A) = 65, #ORD( ABC) = 65.
    #PARM(T) . Returns list of parameters which was assigned when
the whole program called for execution. 
    #DEBUG(E). If E equals the atom 'RULES', then, as soon as the
rule  is  called, information concerning calls of the rules (both
user defined and built-in rules) and their execution results will
be  output.  The  call #DEBUG(NORULES) stops the debugging of the
rules. 


                       12. Sample Compiler

    Compiler  for  the  TOYLAN  language,  which is a very simple
programming  language,  is  discussed  in  the  following  units.
Compiler  works  in  two  passes.  The first phase is parsing and
construction  of  the  program's  intermediate  form  as abstract
syntax tree. The second phase is code generation. 
    Description  of  input  and intermediate language grammars by
means   of   RIGAL   is   presented.   Thus  formalized  compiler
documentation (admitting checking on a computer) is obtained. 


                     12.1 TOYLAN Language

    The   description   of  TOYLAN  syntax  can  be  regarded  as
description  of  acceptable  sequences of tokens. Atoms represent
lexical   elements  of  TOYLAN  program:  keywords,  identifiers,
constants,  operation signs, delimiters. The context free grammar
of TOYLAN can be described in the form of RIGAL program. 
  #PROGRAM
      'PROGRAM' $Id
                 (* #DECLARATION ';' *)  (+ #STATEMENT + ';' )  ##
  #DECLARATION  ('INTEGER'!'BOOLEAN')   (+ $Id  + ',')        ##
  #STATEMENT     ( #ASSIGNMENT      !  #INPUT     !
                   #OUTPUT          !  #CONDITIONAL  )          ##
  #ASSIGNMENT   $Id ':='  #EXPRESSION                         ##
  #INPUT        'GET' '(' (+ $Id  + ',') ')'                   ##
  #OUTPUT       'PUT' '(' (+ #EXPRESSION + ',') ')'            ##
  #CONDITIONAL  'IF' #EXPRESSION   'THEN' (+ #STATEMENT + ';')
                ['ELSE' (+ #STATEMENT + ';' )]    'FI'         ##
  #EXPRESSION   #SUM     [  '='  #SUM  ]                      ##
  #SUM          #FACTOR   (* '+'  #FACTOR   *)                ##
  #FACTOR       #TERM    (* '*' #TERM      *)                 ##
  #TERM         $N ;; -- numeric constant
                ('TRUE' ! 'FALSE' ) ;; -- Boolean constants
                $Id;; -- variable
                '(' #EXPRESSION ')'                           ##
    Context conditions are the following:
    1)  all  variables used in statements and expressions must be
declared, 
    2)  one  and  the  same  variable name should not be declared
twice, 
    3)  left  and  right parts of assignment statement must be of
the same type, 
    4)  operands  of  input-output statements must be of the type
INTEGER. 
    All  variables  of the type INTEGER have initial value 0, and
all variables of the type BOOLEAN have initial value FALSE. 


               12.2 Intermediate Form of Program.
                         Abstract syntax tree

    Special   languages  to  represent  intermediate  results  of
compilation are used in compiler building practice. For instance,
P-code for PASCAL compilers and language DIANA [8] for ADA. 
    The  result  of  the  first phase of the TOYLAN compiler is a
tree.   Let's   call  it  abstract  syntax  tree.  Along  program
components  it  contains  some semantic attributes, for instance,
types  of  expressions. One of the most significant attributes is
table of variables, obtained as a result of parsing of the TOYLAN
program declarations. 
    The  structure  of abstract syntax tree of the TOYLAN program
is described by the following rules. 
  #S_PROGRAM
      'PROGRAM'::<. NAME : $Id,
                     DECLARATIONS : #S_DECLARATIONS ,
                     STATEMENTS   : (.(* #S_STATEMENT *).) .>    ##
  #S_DECLARATIONS    -- variables table
      <* $Id : ( INTEGER ! BOOLEAN )  *>                       ##
  #S_STATEMENT
      ASSIGNMENT :: <. LEFT  : $Id,
                        RIGHT : #S_EXPRESSION   .>   ;;
      INPUT :: (. (* $Id *) .)    ;;
      OUTPUT :: (. (* #S_EXPRESSION *) .)      ;;
      CONDITIONAL :: <. COND : #S_EXPRESSION,
                         THEN : (.(* #S_STATEMENT *).),
                         [ ELSE : (.(* #S_STATEMENT *).)] .>   ##
  #S_EXPRESSION
      COMPARE :: <. ARG1 : #S_EXPRESSION, ARG2 : #S_EXPRESSION,
                     TYPE : BOOLEAN    .>  ;;
      ADD :: <. ARG1 : #S_EXPRESSION,  ARG2 : #S_EXPRESSION,
                 TYPE : INTEGER       .>  ;;
      MULT :: <. ARG1 : #S_EXPRESSION,  ARG2 : #S_EXPRESSION,
                  TYPE : INTEGER       .>  ;;
      <. VARIABLE : $Id ,  TYPE     : ( INTEGER ! BOOLEAN ) .>   ;;
      <. CONSTANT : $N ,   TYPE     : INTEGER .>   ;;
      <. CONSTANT : ( 0 ! 1) , TYPE : BOOLEAN .>            ##


                    12.3 Target Language BAL

    The  goal of the TOYLAN compiler is to obtain program text in
a  low level language BAL. This language is a simplified model of
assembler  languages.  The  memory of BAL-machine is divided into
separate words. Every word may contain an integer, besides, there
are  work registers R0, R1, R2, ... of the word size each. Let us
suppose  the  number  of  registers  to be unlimited, in order to
eliminate the problem of optimal register usage during generation
phase. 
    Command  of  BAL ABC: DEFWORD N reserves a word in the memory
and  imbeds  integer  N in it. We can refer to this word in other
commands by name ABC. 
    Commands  of BAL have two operands which are described by the
name of memory word, by the name of register or by the literal of
the type =NNN, where NNN is an integer. Commands can be marked by
labels. 
  1) MOV A1,A2  This command moves memory word A1 to memory word A2.
  2) LOAD  RI,A  Loading of word A into register RI.
  3) SAVE RI,A Unloading of the contents of register RI into memory
                word A.
  4) ADD RI,A  or  ADD RI,RJ  The sum of operands is imbedded in RI.
  5) MULT RI,A  or  MULT RI,RJ   Multiplication of operands is
                imbedded in RI.
  6) COMPARE RI,A  or  COMPARE RI,RJ  If operand values are equal,
                it is 1 that is imbedded in RI,  if  they  are  not
                equal, 0 is imbedded.
  7) BRANCH RI,M  If the value of RI is equal to 0, then transfer
                to the command marked  by  label  M  takes  place,
                otherwise, to the next command.
  8) JUMP M  Unconditional transfer to the command marked by label M.
  9) EOJ       Completes the execution of the BAL program.
  10) READ A    Reads the integer from standard  input  device  and
                imbeds it in word A.
  11) WRITE A  or  WRITE RI Outputs the integer from memory word or
                from register to standard output device.
  12) NOP      An empty statement.


                  12.4 Main Module of Compiler

    The main program of the TOYLAN compiler contains calls of the
first and second compilation phases and file opening statements. 
 #TOYLAN_COMPILER
    OPEN REP ' ';  --message file is connected with the screen
    $LEXEMS:=#CALL_PAS(35 'A.TOY');
     -- a list of tokens is loaded from the file A.TOY by scanner
    $S_TREE := #A_PROGRAM($LEXEMS);
    -- 1st  phase; result of parsing - abstract syntax tree - is
    -- imbedded in the variable $S_TREE; during parsing messages
    -- about discovered errors in file REP can be output.
    IF $S_TREE  ->  OPEN GEN 'A.BAL'; -- if the tree is created,
         -- then file is opened to output the generated BAL text
                 #G_PROGRAM($S_TREE) -- 2nd phase - code generation
    ELSIF  T  ->  REP << errors are discovered   FI;
    REP << end of compilation                   ##
    The  compilation  listing that contains source text and error
messages  is  not  envisaged in the TOYLAN compiler. Formation of
the listing can be a separate phase. 


                       12.5 Parsing Phase

    The  rule  #A_PROGRAM  carries  out  parsing  of tokens list,
checks  context  conditions,  generates error messages and builds
abstract syntax tree of TOYLAN program. 
    Patterns  of the rule #A_PROGRAM and of the rules subordinate
to  it, actually, coincide with patterns of the rule #PROGRAM and
with  the  associated rules that describe context free grammar of
the TOYLAN language. Just operations to check context conditions,
to  output  error  messages and to construct abstract syntax tree
are added. 
    In   our   parser  diagnostics  is  based  on  the  following
principles.  First of all, "panic" reaction to an error should be
avoided  and  several  messages concerning one and the same error
should  not  be  output  (though,  we  can't  manage  it always),
secondly,  error  neutralization is transition to the analysis of
the next statement, i.e., skip of tokens until the nearest symbol
';'. 



 #A_PROGRAM    -- the rule is applied to the list of tokens
    (. PROGRAM  $Id
         (*  $DECL++:= #A_DECLARATION  ';'  *)
                  --formation of variables table
          (+  $STATEMENTS !.:= #A_STATEMENT   + ';' )
                  --formation of statements list
    .)  / RETURN 'PROGRAM' :: <. NAME : $Id,
                                  DECLARATIONS : $DECL ,
                                  STATEMENTS : $STATEMENTS .>/ ##
 #A_DECLARATION     $TYPE := ( INTEGER ! BOOLEAN )
  (+ $Id /IF LAST #A_PROGRAM $DECL.$Id OR $REZ.$Id ->
              REP << VARIABLE $Id DOUBLE DEFINED  FI;
           $REZ++:= <.$Id : $TYPE .>/  + ',' ) / RETURN $REZ / ##
 #A_STATEMENT   $REZ := ( #A_ASSIGNMENT ! #A_INPUT !
               #A_OUTPUT ! #A_CONDITIONAL )  / RETURN $REZ / ;;

     (* $A!.:=S'($$ <> ';' ) *)   -- skip until nearest ';'
               / REP << UNRECOGNIZED STATEMENT $A /
 ##

 #A_ASSIGNMENT $Id  ':='/ $LPType := LAST #A_PROGRAM $DECL .$Id;
        IF NOT $LPType  -> REP << VARIABLE $Id ' IS NOT DEFINED ' FI /
    $E:= #A_EXPRESSION
         /IF $LPType <> $E . TYPE ->
              REP<< 'LEFT AND RIGHT SIDE TYPES ARE DIFFERENT '
                     'IN ASSIGNMENT STATEMENT ' FI;
         RETURN ASSIGNMENT::<. LEFT: $Id, RIGHT: $E .> /
 ONFAIL IF $LPType  -> REP<< 'WRONG EXPRESSION IN ASSIGNMENT'  FI  ##
 #A_INPUT      GET  '('
    (+ $E !.:= $Id  /IF  LAST #A_PROGRAM $DECL.$Id <> INTEGER ->
            REP << $Id 'IN STATEMENT GET IS NOT OF THE TYPE INTEGER'
                      FI / + ',' ) ')' / RETURN INPUT :: $E /   ##
 #A_OUTPUT   PUT  '(' (+  $C := #A_EXPRESSION  / $E !.:= $C;
              IF  $C . TYPE  <> INTEGER ->
        REP << OPERAND OF PUT STATEMENT IS NOT OF THE TYPE INTEGER
                FI /   + ',' ) ')'/ RETURN OUTPUT :: $E /       ##
 #A_CONDITIONAL     'IF' $BE := #A_EXPRESSION
    /IF $BE . TYPE <> BOOLEAN ->
              REP<< CONDITION IS NOT OF BOOLEAN TYPE FI /
                   'THEN' (+ $P1 !.:= #A_STATEMENT + ';' )
         [ 'ELSE' (+ $P2 !.:= #A_STATEMENT + ';' ) ]      'FI'
    / RETURN CONDITIONAL :: <. COND : $BE , THEN : $P1 ,
                               ELSE : $P2  .> /            ##
 #A_EXPRESSION      $A := #A_SUM   [  '=' $B := #A_SUM
    / $A := COMPARE::<. ARG1 : $A, ARG2 : $B, TYPE : BOOLEAN.>/ ]
    / RETURN $A /           ##
 #A_SUM   $A := #A_FACTOR    (* '+' $B := #A_FACTOR
         / $A := ADD::<. ARG1: $A, ARG2: $B, TYPE: INTEGER .>/ *)
    / RETURN $A  /          ##
 #A_FACTOR  $A := #A_TERM     (* '*' $B := #A_TERM
         /$A := MULT::<. ARG1: $A, ARG2: $B, TYPE: INTEGER .>/ *)
    / RETURN $A /           ##
 #A_TERM
    $N  / RETURN <. CONSTANT : $N , TYPE : INTEGER .>/;;
  ( ( TRUE / $K :=1/ ) ! ( FALSE / $K :=0 / ) )
                 /RETURN <. CONSTANT: $K, TYPE: BOOLEAN .>/  ;;
    $Id  / $X:= LAST #A_PROGRAM $DECL.$Id;
         IF NOT $X  ->  REP << VARIABLE $Id IS NOT DECLARED
         ELSIF T -> RETURN <. VARIABLE: $Id, TYPE: $X .> FI / ;;
    '(' $E := #A_EXPRESSION ')' / RETURN $E /        ##


                  12.6 Code Generation Phase

    Code  generation is performed when traversing abstract syntax
tree. 
    To  avoid  possible  conflicts  between variable names in the
TOYLAN  program  and register names (of the type RNNN) and labels
(of  the  type  LNNN)  in  the object program, variable names are
substituted by standard names of the type VARNNN. 


 #G_PROGRAM    / $LABEL := 0 /   --global variable $LABEL serves
                                 --to generate unique labels.
  PROGRAM::<.DECLARATIONS: $TAB := #TABLE_OF_NUMBERS,
              --creation of the table of unique variable numbers
             STATEMENTS: (.(* #G_STATEMENT *).) / GEN << 'EOJ' /,
             DECLARATIONS : #G_DECLARATIONS      .>       ##
 #TABLE_OF_NUMBERS <* $Id: $TYPE /$N :=$N+1; $T++:=<. $Id: $N.>/ *>
                   /RETURN $T/    ##
 #G_STATEMENT ( #G_ASSIGNMENT ! #G_INPUT !
              #G_OUTPUT     ! #G_CONDITIONAL )      ##
 #G_ASSIGNMENT    ASSIGNMENT::<. LEFT: $Id := #NAME,
  RIGHT :( ( <. VARIABLE: $Id1:=#NAME .>
              /GEN << MOV @ $Id1 ',' $Id / ) !
           ( <. CONSTANT : $N .> /GEN << MOV @ '=' $N ',' $Id /) !
           ( $NREG := #G_EXPRESSION
              /GEN << 'SAVE' @ 'R' $NREG ',' $Id / ) )  .>    ##
 #G_INPUT INPUT::(. (* $Id := #NAME /GEN << READ $Id / *) .)   ##
 #G_OUTPUT     OUTPUT :: (. (*
    ( ( <. VARIABLE : $Id := #NAME .> /GEN << WRITE $Id / ) !
       ( <. CONSTANT : $N .> /GEN << WRITE @ '=' $N / )  !
       ( $NREG := #G_EXPRESSION /GEN << WRITE @ 'R' $NREG /) )
                         *) .)                                ##
 #G_CONDITIONAL     CONDITIONAL ::
     <. COND : $NREG := #G_EXPRESSION
            / $LABEL1 :=#NEW_LABEL(); $LABEL2 :=#NEW_LABEL() /,
         THEN   : / GEN << BRANCH @ 'R' $NREG ',L' $LABEL1  /
                  (. (* #G_STATEMENT *) .)
               / IF $.ELSE -> GEN << JUMP @ 'L' $LABEL2 FI;
                             GEN << @ 'L' $LABEL1 ': NOP' / ,
       [ ELSE : (. (* #G_STATEMENT *) .)
               / GEN << @ 'L' $LABEL2 ': NOP' / ]     .>  ##
 #G_EXPRESSION --returns the number of the register containing
               --result of the evaluation of expression
  $EXPR
 / $NREG := 0 / -- number of the first accessible register
 /RETURN #G_EXPR($EXPR)/
 ##


 #G_EXPR     ( <. VARIABLE: $ID :=#NAME .>  !
              <. CONSTANT: $N / $ID := #IMPLODE('=' $N)/ .>)
            / $REG := COPY( LAST #G_EXPRESSION $NREG ) ;
              GEN << 'LOAD' @ 'R' $REG ',' $ID  ;
              LAST #G_EXPRESSION $NREG + := 1; RETURN $REG /  ;;
         $OP::<. ARG1 : $R1 := #G_EXPR, ARG2 : $R2 := #G_EXPR .>
          / GEN << $OP @ 'R' $R1 ',R' $R2 ; RETURN $R1 /   ##
 #G_DECLARATIONS
 <* $ID: $TYPE /$ID1 := #NAME($ID); GEN<< $ID1 ':' DEFWORD 0 /*> ##
 #NEW_LABEL    --auxiliary rule
    /LAST #G_PROGRAM  $LABEL+:=1;
     RETURN COPY (LAST #G_PROGRAM $LABEL )/              ##
 #NAME    $ID --returns standard name of the variable $ID in $TAB
     / RETURN #IMPLODE( VAR  LAST #G_PROGRAM $TAB.$ID)/    ##


                 13. Conclusions and Future Work

    As  it was demonstrated above, RIGAL supports syntax-oriented
style   of   compiler  design.  Programs  written  in  RIGAL  are
well-structured and it is easy to read and debug them. 
    Our experience [13] proves that the optimizing RIGAL compiler
in  VAX/VMS environment makes it possible to implement production
quality compilers for high level languages. 
    RIGAL  can  be considered as yet another language prototyping
tool  in  the  sense  of  [14], because it allows the designer to
develop an experimental translator in short period of time. 
    Besides  interpreter  for  debugging  purposes and optimizing
compiler  RIGAL support system includes a cross-referencer, which
helps to avoid misuse of global variables. 
    In  order  to  improve  static  and  dynamic  type  checking,
variable  type  descriptions in the form of formal comments would
be added to the language. 
    Taking  in  account  that control structures of RIGAL program
are  very  close  to input data structures, it seems promising to
develop  automatic  and  semiautomatic  methods  for test example
generation for the given RIGAL program. 


                           References

  [1]  A.Aho,  J.Ullman. The  theory  of  parsing,  translation  and
compiling// Prentice-Hall, Inc. Englewood Cliffs,N.J. 1972. -
vol.1,2.
  [2] S.C.Johnson. YACC - Yet  Another  Compiler  Compiler  //  Bell
Laboratories, Murray Hill,N.J., 1978, A technical manual.
  [3] C.H.Koster. Using the CDL Compiler Compiler// Lecture  Notes
in Computer Science , Vol.21, Springer-Verlag, Berlin, 1977.
  [4] I.R.Agamirzyan. Compiler Design Technological Support System SHAG. // Space
mechanics algorithms, Leningrad, vol. 79,1985, pp. 1-53.,(in Russian)
  [5] D.E.Knuth. Semantics of context-free languages//  Mathematical
Systems Theory, 2, 2, 1968, pp.127-146.
  [6] V.A.Serebryakov. Methods of Attribute Translation.// In:
Programming Languages, Moscow, "Nauka", 1985, pp. 47-79,(in Russian).
  [7] A.O.Vooglaid, M.V. Lepp, D.B.Lijb. Input Languages of the
ELMA System. // Proceedings of the Tallin Polytechnical Institute,
(in Russian).
  [8] The intermediate language DIANA : Design and Implementation //Lecture 
Notes in Computer Science, Vol.180, Springer-Verlag,
Berlin, 1984.
  [9] R.Vilhelm. Presentation of the compiler generation system MUG2: 
Examples, global flow analysis and optimization// Le point
sur la compilation, INRIA, 1978, à.307-336.
  [10] Basic REFAL and its implementation on computers.// CNIPIASS,
Moscow, 1977, (in Russian). 
  [11] P.Lucas. Formal definition of programming languages and
systems // IFIP Congress, 1971.
  [12] M.Ganapatti, C.N.Fisher, J.L.Hennessy. Retargetable  compiler
code generation// ACM Computing Survays, 14(4), 1982.
  [13]  J.Barzdin,  A.Kalnins,  M.Auguston,  SDL  tools  for   rapid
prototyping and testing.// in SDL'89 : The language  at  work,
ed.  O.Faergemand  and   M.M.Marques,   North-Holland,   1989,
pp.127-133.
  [14] R.Herndon, V.Berzins, The realizable benefits of  a  language
prototyping  language.//   IEEE   Transactions   on   Software
Engineering , vol.14, No 6, June 1988, pp.803-809
