AspectLISA logo

Institute of Computer Science

Faculty of Electrical Engineering and Computer Science   University of Maribor

Links


Reference manual - LISA specification Languge

AspectLISA logo
uses special specification language which consists of regular definitions, attributes definitions, pointcuts, advice, rules, which are generalized syntax rules that encapsulate semantic rules and methods in semantic domains. The LISA specification language, including apect-oriented features, pointcuts and advice, has the following parts:

language L1 [extends L2, ..., LN ] {
  lexicon {
      [[Q] overrides | [Q] extends] R regular expr.
  }
  attributes type At1, ..., AtM
  pointcut P < [S1, ..., Sr] > L.Y : LhsP ::= RhsP ;   
  advice [[B] extends | [B] overrides] A < [T1, ..., Tr] > on P [, P
1, ..., pn]{
    semantic functions
  }
  rule [[Y] extends | [Y] overrides] Z {
   X ::= X11 X12 ... X1p compute {
     semantic functions
     |
   Xr1 Xr2 ... Xrt compute {
     semantic functions 

   };
  }
   
   method [[N] overrides | [N] extends] M {
   operations on semantic domains
  }
 }

Symbols used in formal AspectLISA specifications above have following meaning:
As can bee seen from formal specifications above each language in LISA specification starts with reserved word language and has unique name and list of parent language(s) (optional).

language Expression {
     // language body
}

language ExpressionOne extends Test {
     // language body
}

Lexical analysis

Lexical part of new language is defined with reserved word lexicon. From that part LISA generates Scanner (Java source code). Tokens are defined with named regular expressions (regular definitions). Each regular expression is named and can be extended or redefined in derived language(s).

In lexical analysis or scanning, the stream of characters representing the source program is read from left to right and grouped into tokens. The lexemes matched by the pattern for the token represent strings of characters in the source program that can be treated together as a lexical unit. Regular expressions, which are the most frequently used formal method for specifying patterns are also used in LISA. More precisely, LISA uses regular definitions where each regular expression is associated with a unique name:

R1 regularexpr1
R2 regularexpr2
Rn regularexprn  


The name of regular expression (Ri) is an identifier which is described with following regular expression [a-zA-Z_][a-zA-Z_0-9]*. Regular expression regularexpri is a sequence of characters until the end of current line (spaces are ignored). Regular expressions describe patterns with whom strings of characters in the source program are matched. LISA uses following rules for describing regular expressions:

expression matches
x the character "x"
\x "x" even if x is a special character
\0xFF character represented by ASCII code
[s] any character in the string s
[x-y] any character in the range from x to y
[^s] any character not in the string s
x* 0 or more instances of x
x+ 1 or more instances of x
x? 0 or 1 instance of x
x|y an x or y
(x) an x
#s an expression defined by s

Where special characters (meta-symbols) in LISA's lexical specifications are:  | \ ( ) * + ? [ ] - # ^
Each symbol followed meta symbol \ is interpreted as it is (\\ is interpreted by LISA as \).


Definition of lexical part starts with reserved word lexicon and it's defined ina block { }. Some examples of regular expressions written in LISA are:

lexicon {
     comment        /\*[^\*]+\*/
     Some_ink       [pr]?ink
     Keyword        Begin | end
     ID             [a-zA-Z_][a-zA-Z0-9_]*
     NUMBER         [0-9]
     Integer        #NUMBER+
     Operator       \+ | \- | \* | / | < | > | = | := | \#
     Delimiter      ; | . | \( | \) | ,
     WhiteSpace     [\ \0x0A\0x0D\0x09]+
     ignore         #comment | #
WhiteSpace
}


The first regular expression describes multi-line comment starts with characters /* and ends with */ (e.g. /* this is comment */). It is important to notice that above regular expression for comment do not allow that characters between /* and */ contain the character *. The second regular expression matches with the words pink, rink, and ink; but not with prink. The third regular expression matches with the words Begin and end. Other regular expressions above are simple and do not need extra explanation. The last regular definition ignore defines regular expressions which are ignored by scanner (this tokens are not returned by scanner).

Lexicon part of LISA specifications can be extended using inheritance. 

lexicon {
  extends Some_ink  [lm]?ink  // regular expression Some_ink now
                              // matches also
link and mink
}

Syntax analysis

In syntax analysis, tokens of a source program are grouped into grammatical phrases. The task of the syntax analyzer or parser is to determine if a string of tokens can be generated by a grammar phrase. The syntax of the programming language is usually described by the well known BNF notation. In LISA standard BNF conventions are used; context-free productions are specified in the rule part of language definition using following conventions:
X ::= X11 X12 ... X1p
|
X21 X22 ... X2r
|
|
Xn1 Xn2 ... Xns
;

where X is a left-hand nonterminal and Xij is a terminal or nonterminal symbols. To distinguish between nonterminal and terminal symbols LISA uses following convention. Nonterminal is described with following regular expression [A-Z_][A-Z_0-9]*. Therefore, first character must be capital letter followed by capital letters, digits or character _ . All other symbols are terminal symbols. Empty production is written as: X :: = epsilon.

The BNF for arithmetic expressions written in LISA is:

 E  ::= T EE ;
 EE ::= + T EE | epsilon ;
 T  ::= F1 TT ;
 TT ::= * F1 TT | epsilon ;
 F1 ::= #Integer ;
 F1 ::= ( E ) ;


Semantic analysis

When the syntax of sentences is correct the meaning of sentences or semantics can be computed. The meaning of programs in LISA is described with attribute grammars. An attribute grammar is based on a context-free grammar and associates attributes with the nodes of a parse tree, thus obtaining an attributed or semantic tree. Attribute evaluation rules are associated with the each context-free production. In LISA attribute evaluation rules are written in a block { } which start with keyword compute. This block is inserted between the last symbol of particular production and the character ; or |. Semantic rule in LISA is actually Java assignment statement. To increase modularity of specifications and to enable inheritance of syntax/semantic specifications in LISA these features are part of
generalized LISA syntax rules. Each generalized LISA rule has unique name and stars with reserved word rule. For example, to the first two above productions the following semantic rules are associated:

rule Exp {
E ::= T EE compute {
              E.val = EE.val;
              EE.inVal = T.val;
           };
}

rule Exp1 {
EE ::= + T EE compute {
                EE[0].val = EE[1].val;
                EE[1].inVal = EE[0].inVal + T.val;
              }
     | epsilon compute {
                EE.val = EE.inVal;
               };
}

Example of inheritance of semantics:

language A {
  lexicon {
    term   a | b
  }
  attributes int *.a;  
  rule X {
    P ::= a A.P P compute {
      P.a = 1 + P[1].a + P[2].a;
// A.P - early bind
    } ; 
  }  
  rule Y {
    P ::= b compute {
      P.a = 1;
    } ; 
  }
}

language
B extends A {
  rule START {
    G ::= P compute {
      G.a = P.a;
    };
  }
  rule extends Y {
    compute {
       P.a = 0;
    }
  }
}


From specifications above the following monolithic attribute grammar is generated (details can be found here):

B.G = B.P compute { B.G[0].a = B.P[0].a; };
B.P ::= b compute {B.P[a] = 0;}
// B.P ::= a A.P B.P compute { B.P[0].a = 1 + A.P[1].a + B.P[2].a;};
// error indexes must be re-computed
B.P ::= a A.P B.P compute { B.P[0].a = 1 + A.P[0].a + B.P[1].a;};  // O.K.
A.P ::= a A.P A.P compute { A.P[0].a = 1 + A.P[1].a + A.P[2].a;};
A.P ::= b compute {A.P[a] = 1;}



Attributes


Attributes in the node can be of two kinds: the inherited attributes, whose values are obtained from the siblings and the parent of that node in the parse tree, and the synthesized attributes, whose values are obtained from the children of that node in the parse tree. The type of attributes (inherited or synthesized) is derived by LISA an hence is no need to be specified by LISA user. However, the type of attributes and nonterminals to which attributes are attached have to be specified. In LISA the type of attribute can be any valid Java type. In LISA attributes are defined in the block which starts with the keyword attributes. Usually, the same attribute name is attached to many different nonterminals. To avoid unpleasant repeating the wild character * can be used instead. An example of attribute definitions for above example is:

attributes int *.val, EE.inVal, TT.inVal;

Attributes can be inherited from ancestor specifications, but cannot be extended. On the other hand, it is possible that current specifications override ancestor attributes.

Pointcut definitions

Pointcuts
are defined using reserved word pointcut. Each pointcut has a unique name and a list of parameters (terminals and non-terminals used in semantic functions of advice). 
In the pointcut definition one can use two wildcards. The wildcard '..' matches zero or more terminal or non terminal symbols and can be used only to specify right-hand side matching rules. The wildcard '*' is used to match parts or whole literal representing a symbol.

pointcut First<T, EE> Expression.* :  *   ::=  *  EE ;
pointcut Second<T, EE>         *.* :  EE  ::=  ..  EE ;


Inheritance of pointcuts is defined in similar manner as for attributes. Pointcuts as well as attributes cannot be extended, but can be inherited from ancestor attribute grammars. It is possible that some pointcut is redefined in current specifications which override pointcut specified  in ancestor specifications.


Advice


Advice in AspectLISA are defined using reserved word advice. Each advice has unique name, list of formal parameters (name can be the same if number of parameters differs) and contains information about the pointcut where advice semmantics will appear. An example of advice which is attached to pointcut 'First' is shown below:

advice Beg<a, b> on First {
  a.val = b.val + 1;
}


Advice can be applied on several pointcuts in same language or in parent language(s). Error appears if same advice should to be applied to several pointcuts.

Advice inheritance is defined in a similar manner as for semantic rules (both defines semantics).

advice extends Beg        // semantics of that advice will be applied on same
                          // pointcut as ancestor advice
advice Begin extends Beg  // same as previous example; advice has different name
advice overrides Beg      // only semantics of this advice will be applied on
                          // specific pointcut; overriden advice cannot be
                          // applied to any pointcut after has been overriden
                          // once
advice extends Lx.Beg     // this advice extend advice Beg of language Lx
             


Operations On Semantic Domains

Additional semantic calculations can be implemented in method part of LISA specifications. Method part starts with reserved word method followed by an unique name. All operations must be defined beetwen { }. Method part consists of native Java code (legal Java code == legal LISA method code). Java methods defined in method part of specifications can be used in semantic equations (LISA generalized rules).

rule someRule {
   A ::= a A compute {
       A.attr = calculate(A[1].attr);
   }
}

method SomeMethod {
   import java.util.Hashtable;

   public 
Hashtable calculate(Hashtable aAttr) {
      return someHashtable;
   }

}

Operations on semantic domains are also subjet of inheritance. You can extends or defeat (change) methods inside method part in extended Languages. In the example below, method calculate will be replaced with new version.

method overrides SomeMethod {
   public Hashtable calculate(Hashtable aAttr) {
      return someNewHashtable;
   }
     
}