Reference
manual - LISA specification Languge
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 [, P1,
..., 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:
- L - language name,
- Q and R - regular expression name,
- At - attribute name,
-
P - pointcut name,
- S -
actual symbol,
- LhsP and
RhsP - left and right-hand side of pointcut production,
- A - advice name,
-
T - formal symbol,
- X -
grammar symbol,
- Y and Z
- grammar rules,
- N and
M - method name.
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;
}
}