|
The declaration syntax in gbeta is very simple and consistent. It is
also based on "funny" characters, and this makes it hard for the
casual reader to understand what is going on in a gbeta program,
since the reader's basic assumptions about the meaning of syntax do
not reveal enough. The trick is to focus on the few "funny"
characters right after the colon, they tell what kind of attribute is
being declared here.
The syntax specifications below mention the <Merge>
and the <AttributeDenotation> syntax categories.
For now, think of a <Merge> as a main part or an
identifier, and think of an <AttributeDenotation>
as an identifier. The examples go slightly beyond what has been
presented sofar, so don't despair if some of the examples can not be
fully explained by now.
Declarations of patterns
A pattern is declared by the simplest form of declarations:
A pattern is the language concept in gbeta which takes care of every
aspect of structure description. Whenever a piece of substance is
created, its structure is created according to some pattern, and that
again consists of predefined and/or main part specified building
blocks. To be precise, a pattern also specifies a run-time context,
i.e. one or more enclosing objects (origins ). Since a
pattern includes the specification of some enclosing objects, it does
not exist before run-time, and patterns associated with the same
syntax (main parts) are still different if their enclosing objects are
different. The static analysis works with patterns relative to a
given run-time environment: take the analysis version of a pattern
and provide it with a concrete run-time context, and there is your
pattern! The fact that the run-time context is included into patterns
makes it possible to create substance directly from patterns. Here's
an example of some pattern declarations:
p: (# q: (# #);
r: (# s: (# #);
t: (# #)
#);
u: boolean
#);
v: (# #) |
Declarations of objects
Objects (part objects) are declared using the "@"
substance flag:
A variant declaration specifies that the object should have its own
execution stack:
<Names> ":" "@" "|" <Merge> |
This is used when creating concurrent threads or co-routines.
A few examples:
i,j: @integer;
f: @real;
y: @|(# g: @real do INNER #);
x: @(# k,l.m: @boolean #) & somePattern |
Declarations of object references
To allow a name to refer to varying objects during the execution of a
program, it is necessary to introduce the notion of object
identity. When using the "@" substance flag, a name
is declared to denote an object, and that's it, but when using the
"^" object identity flag, the name is declared to denote
the identity of an object, and by changing the state of this
attribute, it lets the name refer to different objects at different
times, or possibly to "no object," NONE .
<Names> ":" "^" <AttributeDenotation> |
Again, there is a concurrency biased version:
<Names> ":" "^" "|" <AttributeDenotation> |
The headline of this section mentions "references" since object
identity attributes are often implemented by pointers to memory cells
and often denoted "pointers" or "references." Hence, we will use the
terms object reference attribute or just object
reference.
The <AttributeDenotation> on the right hand side of
the declaration specifies the qualification of the object
reference attribute. The qualification is a type constraint on the
objects which can be referred to by the attribute. The constraint is
that the pattern of the referred object should be a specialization of
the pattern of the qualification. In other words, the qualification
promises a certain richness of the interface, and the actual object
will at all times support an interface which is at least that rich.
A few examples are:
ir1,ir2: ^integer;
pr: ^p;
conc_pr: ^|p; |
Declarations of virtual patterns
A virtual pattern is a pattern. But a virtual declaration does not
declare the precise pattern value, it declares an upper bound on the
type (or a lower bound on the structure) denoted by the declared
name. We'll return to this in more detail
later. there are three variants; the virtual declaration:
.. the virtual further-binding:
<Name> ":" ":" "<" <Merge> |
.. and the virtual final-binding:
They look like this:
p: (# v:< (# #)#);
q: p(# v::< (# #)#);
r: q(# v:: (# #)#) |
Declarations of pattern references
Just like we can have a name which denotes an object and another name
which may denote different objects at different times, we can also
have the dynamically varying version of a pattern declaration:
<Names> ":" "##" <AttributeDenotation> |
This declares the names to be attributes whose values are patterns.
The patterns must be specializations of the pattern denoted by the
right hand side of the colon. This is also covered in more detail
later. An example is:
Declarations of repetitions
A
repetition in gbeta corresponds to what is often
designated an array in other languages. Most attributes can be
declared in repeated versions:
<Name> ":" "[" <Index> "]" "@" <Merge> |
<Name> ":" "[" <Index> "]" "@" "|" <Merge> |
<Name> ":" "[" <Index> "]" "^"
<AttributeDenotation> |
<Name> ":" "[" <Index> "]" "^" "|"
<AttributeDenotation> |
<Name> ":" "[" <Index> "]" "##"
<AttributeDenotation> |
The <Index> is an expression whose
(integer ) value is obtained when the
enclosing object is instantiated, and that becomes the initial number
of elements in the repetition. In practice, it looks like this:
txt: [100] @char;
conc_xrs: [a+b] ^|x;
prs: [0] ##p |
The next section introduces the notion of coercion
which denotes the transformation processes that are used to obtain a
certain category of entity (e.g. a pattern) from a given entity
(e.g. an object). In other words, coercion compensates for the fact
that sometimes a declaration has another "funny character" flag than
what is needed in the usage context.
| |